blob: e567229664f0f61ec926bbaf534d6a26712287d5 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/of_device.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/of_address.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/amlogic/irblaster.h>
#include <linux/amlogic/irblaster_consumer.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
/* Amlogic AO_IR_BLASTER_ADDR0 bits */
#define BLASTER_BUSY BIT(26)
#define BLASTER_FIFO_FULL BIT(25)
#define BLASTER_FIFO_EMPTY BIT(24)
#define BLASTER_FIFO_LEVEL (0xff << 16)
#define BLASTER_MODULATOR_TB_SYSTEM_CLOCK (0x0 << 12)
#define BLASTER_MODULATOR_TB_XTAL3_TICK (0x1 << 12)
#define BLASTER_MODULATOR_TB_1US_TICK (0x2 << 12)
#define BLASTER_MODULATOR_TB_10US_TICK (0x3 << 12)
#define BLASTER_SLOW_CLOCK_DIV (0xff << 4)
#define BLASTER_SLOW_CLOCK_MODE BIT(3)
#define BLASTER_INIT_HIGH BIT(2)
#define BLASTER_INIT_LOW BIT(1)
#define BLASTER_ENABLE BIT(0)
/* Amlogic AO_IR_BLASTER_ADDR1 bits */
#define BLASTER_MODULATION_LOW_COUNT(c) ((c) << 16)
#define BLASTER_MODULATION_HIGH_COUNT(c) ((c) << 0)
/* Amlogic AO_IR_BLASTER_ADDR2 bits */
#define BLASTER_WRITE_FIFO BIT(16)
#define BLASTER_MODULATION_ENABLE BIT(12)
#define BLASTER_TIMEBASE_1US (0x0 << 10)
#define BLASTER_TIMEBASE_10US (0x1 << 10)
#define BLASTER_TIMEBASE_100US (0x2 << 10)
#define BLASTER_TIMEBASE_MODULATION_CLOCK (0x3 << 10)
/* Amlogic AO_IR_BLASTER_ADDR3 bits */
#define BLASTER_FIFO_THD_PENDING BIT(16)
#define BLASTER_FIFO_IRQ_ENABLE BIT(8)
#define BLASTER_FIFO_IRQ_THRESHOLD(c) (((c) & 0xff) << 0)
#define DEFAULT_CARRIER_FREQ (38000)
#define DEFAULT_DUTY_CYCLE (50)
#define LIMIT_DUTY (25)
#define MAX_DUTY (75)
#define LIMIT_FREQ (25000)
#define MAX_FREQ (60000)
#define COUNT_DELAY_MASK (0X3ff)
#define TIMEBASE_SHIFT (10)
#define BLASTER_KFIFO_SIZE (4)
#define AO_IR_BLASTER_ADDR0 (0x0)
#define AO_IR_BLASTER_ADDR1 (0x4)
#define AO_IR_BLASTER_ADDR2 (0x8)
#define AO_IR_BLASTER_ADDR3 (0xc)
#define AO_RTI_GEN_CTNL_REG0 (0x0)
#define CONSUMERIR_TRANSMIT 0x5500
#define GET_CARRIER 0x5501
#define SET_CARRIER 0x5502
#define SET_DUTYCYCLE 0x5503
#define BLASTER_TIMEBASE_COUNT (BIT(10) - 1)
#define BLASTER_DEBUG_HIGH (2)
#define BLASTER_DELAY_MS 25
struct meson_irblaster_dev {
struct device *dev;
struct work_struct blaster_work;
struct irblaster_chip chip;
struct completion blaster_completion;
unsigned int count;
unsigned int irq;
unsigned int buffer_size;
unsigned int *buffer;
spinlock_t irblaster_lock; /* use to send data */
void __iomem *reg_base;
void __iomem *reset_base;
};
#define IROUTDebug(fmt, x...)
static void meson_irblaster_tasklet(unsigned long data);
DECLARE_TASKLET_DISABLED(irblaster_tasklet, meson_irblaster_tasklet, 0);
static struct meson_irblaster_dev *
to_meson_irblaster(struct irblaster_chip *chip)
{
return container_of(chip, struct meson_irblaster_dev, chip);
}
static void blaster_initialize(struct meson_irblaster_dev *dev)
{
unsigned int carrier_cycle = DIV_ROUND_CLOSEST(1000,
(dev->chip.state.freq / 1000));
unsigned int high_ct, low_ct;
/*
*1. disable ir blaster
*2. set the modulator_tb = 2'10; mpeg_1uS_tick 1us
*3. set initializes the output to be high
*/
writel_relaxed((~BLASTER_ENABLE) & (BLASTER_MODULATOR_TB_1US_TICK |
BLASTER_INIT_HIGH), dev->reg_base + AO_IR_BLASTER_ADDR0);
/*
*1. set mod_high_count = 13
*2. set mod_low_count = 13
*3. 60khz-8us, 38k-13us
*/
high_ct = carrier_cycle * dev->chip.state.duty / 100;
low_ct = carrier_cycle - high_ct;
writel_relaxed((BLASTER_MODULATION_LOW_COUNT(low_ct - 1) |
BLASTER_MODULATION_HIGH_COUNT(high_ct - 1)),
dev->reg_base + AO_IR_BLASTER_ADDR1);
/*mask initialize output to be high*/
writel_relaxed(readl_relaxed(dev->reg_base + AO_IR_BLASTER_ADDR0) &
~BLASTER_INIT_HIGH,
dev->reg_base + AO_IR_BLASTER_ADDR0);
/*
*1. set fifo irq enable
*2. set fifo irq threshold
*/
writel_relaxed(BLASTER_FIFO_IRQ_ENABLE |
BLASTER_FIFO_IRQ_THRESHOLD(8),
dev->reg_base + AO_IR_BLASTER_ADDR3);
/*enable irblaster*/
writel_relaxed(readl_relaxed(dev->reg_base + AO_IR_BLASTER_ADDR0) |
BLASTER_ENABLE,
dev->reg_base + AO_IR_BLASTER_ADDR0);
}
static int write_to_fifo(struct meson_irblaster_dev *dev,
unsigned int hightime,
unsigned int lowtime)
{
unsigned int count_delay;
unsigned int cycle = DIV_ROUND_CLOSEST(1000,
(dev->chip.state.freq / 1000));
u32 val;
int n = 0;
int tb[3] = {
1, 10, 100
};
/*
* hightime: modulator signal.
* MODULATOR_TB:
* 00: system clock
* 01: mpeg_xtal3_tick
* 10: mpeg_1uS_tick
* 11: mpeg_10uS_tick
*
* AO_IR_BLASTER_ADDR2
* bit12: output level(or modulation enable/disable:1=enable)
* bit[11:10]: Timebase :
* 00=1us
* 01=10us
* 10=100us
* 11=Modulator clock
* bit[9:0]: Count of timebase units to delay
*/
count_delay = ((hightime + cycle / 2) / cycle) - 1;
if (count_delay < 1024) {
count_delay &= COUNT_DELAY_MASK;
val = (BLASTER_WRITE_FIFO | BLASTER_MODULATION_ENABLE |
BLASTER_TIMEBASE_MODULATION_CLOCK | (count_delay << 0));
} else {
count_delay = (((hightime + 100 / 2) / 100) - 1)
& COUNT_DELAY_MASK;
val = (BLASTER_WRITE_FIFO | BLASTER_MODULATION_ENABLE |
BLASTER_TIMEBASE_100US | (count_delay << 0));
}
writel_relaxed(val, dev->reg_base + AO_IR_BLASTER_ADDR2);
/*
* lowtime<1024,n=0,timebase=1us
* 1024<=lowtime<10240,n=1,timebase=10us
* 10240<=lowtime,n=2,timebase=100us
*/
n = lowtime >> 10;
if (n > 0 && n < 10)
n = 1;
else if (n >= 10)
n = 2;
lowtime = (lowtime + (tb[n] >> 1)) / tb[n];
count_delay = (lowtime - 1) & COUNT_DELAY_MASK;
val = (BLASTER_WRITE_FIFO & (~BLASTER_MODULATION_ENABLE)) |
(n << TIMEBASE_SHIFT) | (count_delay << 0);
writel_relaxed(val, dev->reg_base + AO_IR_BLASTER_ADDR2);
return 0;
}
static int write_to_high_fifo(struct meson_irblaster_dev *dev,
unsigned int hightime)
{
unsigned int count_delay;
unsigned int cycle = DIV_ROUND_CLOSEST(1000,
(dev->chip.state.freq / 1000));
u32 val;
/*
* hightime: modulator signal.
* MODULATOR_TB:
* 00: system clock
* 01: mpeg_xtal3_tick
* 10: mpeg_1uS_tick
* 11: mpeg_10uS_tick
*
* AO_IR_BLASTER_ADDR2
* bit12: output level(or modulation enable/disable:1=enable)
* bit[11:10]: Timebase :
* 00=1us
* 01=10us
* 10=100us
* 11=Modulator clock
* bit[9:0]: Count of timebase units to delay
*/
count_delay = (((hightime + cycle / 2) / cycle) - 1) & COUNT_DELAY_MASK;
val = (BLASTER_WRITE_FIFO | BLASTER_MODULATION_ENABLE |
BLASTER_TIMEBASE_MODULATION_CLOCK | (count_delay << 0));
writel_relaxed(val, dev->reg_base + AO_IR_BLASTER_ADDR2);
return 0;
}
static void send_all_data(struct meson_irblaster_dev *dev)
{
int i, j;
unsigned int *pdata = NULL;
unsigned long flags;
unsigned int cycle, max_time, high_ct, low_ct;
cycle = DIV_ROUND_CLOSEST(1000, (dev->chip.state.freq / 1000));
high_ct = cycle * dev->chip.state.duty / 100;
low_ct = cycle - high_ct;
max_time = (high_ct + low_ct) * BLASTER_TIMEBASE_COUNT;
IROUTDebug("send data max_time = %d\n", max_time);
pdata = &dev->buffer[dev->count];
spin_lock_irqsave(&dev->irblaster_lock, flags);
for (i = 0; (i < 120) && (dev->count < dev->buffer_size);) {
if (*pdata > max_time) {
for (j = 0; j < (*pdata / max_time); j++) {
write_to_high_fifo(dev, max_time);
i++;
}
write_to_fifo(dev, *pdata % max_time, *(pdata + 1));
} else {
write_to_fifo(dev, *pdata, *(pdata + 1));
}
pdata += 2;
dev->count += 2;
i += 2;
}
spin_unlock_irqrestore(&dev->irblaster_lock, flags);
}
int meson_irblaster_send(struct irblaster_chip *chip,
unsigned int *data,
unsigned int len)
{
int ret, i, sum_time = 0;
unsigned int high_ct, low_ct, cycle, long_len = 0;
struct meson_irblaster_dev *irblaster_dev = to_meson_irblaster(chip);
pm_runtime_get_sync(chip->dev);
init_completion(&irblaster_dev->blaster_completion);
/*
* 1. set mod_high_count = 13
* 2. set mod_low_count = 13
* 3. 60khz-8us, 38k-13us
*/
cycle = DIV_ROUND_CLOSEST(1000,
(irblaster_dev->chip.state.freq / 1000));
high_ct = cycle * irblaster_dev->chip.state.duty / 100;
low_ct = cycle - high_ct;
writel_relaxed((BLASTER_MODULATION_LOW_COUNT(low_ct - 1) |
BLASTER_MODULATION_HIGH_COUNT(high_ct - 1)),
irblaster_dev->reg_base + AO_IR_BLASTER_ADDR1);
for (i = 0; i < len; i++) {
sum_time += data[i];
if (irblaster_debug) {
if (irblaster_debug >= BLASTER_DEBUG_HIGH)
IROUTDebug("ir[%d] = %d\n", i, data[i]);
if (data[i] > (high_ct + low_ct)
* BLASTER_TIMEBASE_COUNT)
long_len += DIV_ROUND_UP(data[i],
(high_ct + low_ct) *
BLASTER_TIMEBASE_COUNT);
}
}
irblaster_dev->buffer = data;
irblaster_dev->buffer_size = len;
irblaster_dev->count = 0;
send_all_data(irblaster_dev);
IROUTDebug("wait time = %dus len = %d fifosize = %d\n",
sum_time, irblaster_dev->buffer_size, len + long_len);
ret = wait_for_completion_interruptible_timeout
(&irblaster_dev->blaster_completion,
msecs_to_jiffies(sum_time / 1000 + BLASTER_DELAY_MS));
if (!ret) {
pr_err("failed to send all data ret = %d\n", ret);
return -ETIMEDOUT;
}
IROUTDebug("send finish!\n");
pm_runtime_put_sync(chip->dev);
return 0;
}
static struct irblaster_ops meson_irblaster_ops = {
.send = meson_irblaster_send,
};
static void meson_irblaster_tasklet(unsigned long data)
{
struct meson_irblaster_dev *dev = (struct meson_irblaster_dev *)data;
if (dev->count < dev->buffer_size)
send_all_data(dev);
}
static irqreturn_t meson_blaster_interrupt(int irq, void *dev_id)
{
struct meson_irblaster_dev *dev = dev_id;
if (irblaster_debug >= BLASTER_DEBUG_HIGH)
IROUTDebug("enter irq count = %d size = %d\n",
dev->count, dev->buffer_size);
/*clear pending bit*/
writel_relaxed(readl_relaxed(dev->reg_base + AO_IR_BLASTER_ADDR3) &
(~BLASTER_FIFO_THD_PENDING),
dev->reg_base + AO_IR_BLASTER_ADDR3);
if (dev->count >= dev->buffer_size) {
complete(&dev->blaster_completion);
return IRQ_HANDLED;
}
tasklet_schedule(&irblaster_tasklet);
return IRQ_HANDLED;
}
static int meson_irblaster_probe(struct platform_device *pdev)
{
struct meson_irblaster_dev *irblaster_dev = NULL;
struct resource *reg_mem = NULL;
void __iomem *reg_base = NULL;
int err, ret;
if (!pdev->dev.of_node) {
dev_err(&pdev->dev, "pdev->dev.of_node == NULL!\n");
return -EINVAL;
}
irblaster_dev = devm_kzalloc(&pdev->dev,
sizeof(struct meson_irblaster_dev),
GFP_KERNEL);
if (!irblaster_dev)
return -ENOMEM;
spin_lock_init(&irblaster_dev->irblaster_lock);
platform_set_drvdata(pdev, irblaster_dev);
irblaster_dev->dev = &pdev->dev;
reg_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!IS_ERR_OR_NULL(reg_mem)) {
reg_base = devm_ioremap_resource(&pdev->dev, reg_mem);
if (IS_ERR(reg_base)) {
dev_err(&pdev->dev, "reg0: cannot obtain I/O memory region.\n");
return PTR_ERR(reg_base);
}
} else {
dev_err(&pdev->dev, "get IORESOURCE_MEM error.\n");
return PTR_ERR(reg_base);
}
irblaster_dev->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
if (!irblaster_dev->irq) {
dev_err(&pdev->dev, "irq: Failed to request irq number.\n");
return -ENODEV;
}
init_completion(&irblaster_dev->blaster_completion);
ret = devm_request_irq(&pdev->dev, irblaster_dev->irq,
meson_blaster_interrupt,
IRQF_TRIGGER_RISING,
dev_name(&pdev->dev),
irblaster_dev);
if (ret) {
pr_err("Failed to request irq.\n");
return ret;
}
/* init irblaster sysfs */
ret = irblaster_sysfs_init();
if (ret) {
pr_err("Failed to init irblaster sysfs.\n");
return ret;
}
irblaster_dev->reg_base = reg_base;
irblaster_dev->chip.dev = &pdev->dev;
irblaster_dev->chip.ops = &meson_irblaster_ops;
irblaster_dev->chip.of_irblaster_n_cells = 2;
irblaster_dev->chip.state.freq = DEFAULT_CARRIER_FREQ;
irblaster_dev->chip.state.duty = DEFAULT_DUTY_CYCLE;
err = irblasterchip_add(&irblaster_dev->chip);
if (err < 0) {
dev_err(&pdev->dev, "failed to register irblaster chip: %d\n",
err);
return err;
}
irblaster_tasklet.data = (unsigned long)irblaster_dev;
tasklet_enable(&irblaster_tasklet);
/*initial blaster*/
blaster_initialize(irblaster_dev);
pm_runtime_enable(&pdev->dev);
return 0;
}
static int meson_irblaster_remove(struct platform_device *pdev)
{
struct meson_irblaster_dev *irblaster_dev = platform_get_drvdata(pdev);
tasklet_disable(&irblaster_tasklet);
tasklet_kill(&irblaster_tasklet);
irblasterchip_remove(&irblaster_dev->chip);
return 0;
}
static int meson_irblaster_runtime_suspend(struct device *dev)
{
return 0;
}
static int meson_irblaster_runtime_resume(struct device *dev)
{
struct meson_irblaster_dev *irblaster_dev = dev_get_drvdata(dev);
/*initial blaster*/
blaster_initialize(irblaster_dev);
return 0;
}
static const struct of_device_id irblaster_dt_match[] = {
{
.compatible = "amlogic, meson_irblaster",
},
{},
};
MODULE_DEVICE_TABLE(of, irblaster_dt_match);
static const struct dev_pm_ops meson_irblaster_pm_ops = {
SET_RUNTIME_PM_OPS(meson_irblaster_runtime_suspend,
meson_irblaster_runtime_resume, NULL)
};
static struct platform_driver meson_irblaster_driver = {
.probe = meson_irblaster_probe,
.remove = meson_irblaster_remove,
.driver = {
.name = "meson_irblaster",
.owner = THIS_MODULE,
.of_match_table = irblaster_dt_match,
.pm = &meson_irblaster_pm_ops,
},
};
static int __init meson_irblaster_init(void)
{
return platform_driver_register(&meson_irblaster_driver);
}
static void __exit meson_irblaster_exit(void)
{
platform_driver_unregister(&meson_irblaster_driver);
}
fs_initcall_sync(meson_irblaster_init);
module_exit(meson_irblaster_exit);
MODULE_AUTHOR("Amlogic, Inc.");
MODULE_DESCRIPTION("Amlogic ir blaster driver");
MODULE_LICENSE("GPL");