// SPDX-License-Identifier: GPL-2.0-only /* * Driver for ArtInChip CIR controller * * Copyright (C) 2021 Artinchip Technology Co., Ltd. * Authors: Weijie Ding */ #include #include #include #include #include #include #include #include #define AIC_IR_DEV "aic-ir" #define DEFAULT_FREQ 38000 #define US_PER_SEC 1000000UL /* Register definition */ #define CIR_MCR_REG 0x00 #define CIR_MCR_TXFIFO_CLR BIT(17) #define CIR_MCR_RXFIFO_CLR BIT(16) #define CIR_MCR_TX_STOP BIT(9) #define CIR_MCR_TX_START BIT(8) #define CIR_MCR_TX_EN BIT(1) #define CIR_MCR_RX_EN BIT(0) #define CIR_INTR_REG 0x04 #define CIR_INTR_TXB_AVL_INT BIT(6) #define CIR_INTR_TXEND_INT BIT(5) #define CIR_INTR_TX_UNF_INT BIT(4) #define CIR_INTR_RXB_AVL_INT BIT(2) #define CIR_INTR_RX_END_INT BIT(1) #define CIR_INTR_RX_OVF_INT BIT(0) #define CIR_INTEN_REG 0x08 #define CIR_INTEN_TXB_EMPTY_LEVEL_POS (16) #define CIR_INTEN_TXB_EMPTY_LEVEL(x) ((x) << CIR_INTEN_TXB_EMPTY_LEVEL_POS) #define CIR_INTEN_RXB_AVL_LEVEL_POS (8) #define CIR_INTEN_RXB_AVL_LEVEL(x) ((x) << CIR_INTEN_RXB_AVL_LEVEL_POS) #define CIR_INTEN_TXB_AVL_EN BIT(6) #define CIR_INTEN_TXEND_EN BIT(5) #define CIR_INTEN_TX_UNF_EN BIT(4) #define CIR_INTEN_RXB_AVL_EN BIT(2) #define CIR_INTEN_RXEND_EN BIT(1) #define CIR_INTEN_RX_OVF_EN BIT(0) #define CIR_TXSTAT_REG 0x0C #define CIR_TXSTAT_TX_STA (16) #define CIR_TXSTAT_TXFIFO_ERR (10) #define CIR_TXSTAT_TXFIFO_FULL (9) #define CIR_TXSTAT_TXFIFO_EMPTY BIT(8) #define CIR_TXSTAT_TXFIFO_DLEN_MASK GENMASK(6, 0) #define CIR_RXSTAT_REG 0x10 #define CIR_RXSTAT_RX_STA (16) #define CIR_RXSTAT_RXFIFO_ERR (10) #define CIR_RXSTAT_RXFIFO_FULL (9) #define CIR_RXSTAT_RXFIFO_EMPTY BIT(8) #define CIR_RXSTAT_RXFIFO_DLEN (0) #define CIR_RXCLK_REG 0x14 #define CIR_RX_THRES_REG 0x18 #define CIR_RX_THRES_ACTIVE (16) #define CIR_RX_THRES_ACTIVE_LEVEL(x) ((x) << CIR_RX_THRES_ACTIVE) #define CIR_RX_THRES_IDLE (0) #define CIR_RX_THRES_IDLE_LEVEL(x) ((x) << CIR_RX_THRES_IDLE) #define CIR_RX_CFG_REG 0x1C #define CIR_RX_CFG_NOISE_V1 (16) #define CIR_RX_CFG_NOISE_MASK_V1 0xFFFF #define RX_NOISE_LEVEL_V1 0x1770 #define CIR_RX_CFG_NOISE (8) #define CIR_RX_CFG_NOISE_MASK 0xFF #define RX_NOISE_LEVEL 0xFF #define CIR_RX_CFG_NOISE_LEVEL(x) \ ((x & CIR_RX_CFG_NOISE_MASK) << CIR_RX_CFG_NOISE) #define CIR_RX_CFG_NOISE_LEVEL_V1(x) \ ((x & CIR_RX_CFG_NOISE_MASK_V1) << CIR_RX_CFG_NOISE_V1) #define CIR_RX_CFG_RX_LEVEL BIT(1) #define CIR_RX_CFG_RX_INVERT BIT(0) #define CIR_TX_CFG_REG 0x20 #define CIR_TX_CFG_TX_MODE BIT(2) #define CIR_TX_CFG_TX_OUT_MODE BIT(1) #define CIR_TX_CFG_TX_INVERT BIT(0) #define CIR_TIDC_REG 0x24 #define CIR_CARR_CFG_REG 0x2C #define CIR_CARR_CFG_HIGH (16) #define CIR_CARR_CFG_HIGH_VAL(x) ((x) << CIR_CARR_CFG_HIGH) #define CIR_CARR_CFG_LOW (0) #define CIR_CARR_CFG_LOW_VAL(x) ((x) << CIR_CARR_CFG_LOW) #define CIR_RXFIFO_REG 0x30 #define CIR_TXFIFO_REG 0x80 #define CIR_VERSION_REG 0xFFC #define CIR_TXFIFO_SIZE 0x80 struct aic_ir { spinlock_t ir_lock; struct rc_dev *rc; void __iomem *base; struct clk *clk; struct reset_control *rst; const char *map_name; unsigned int tx_duty; int irq; u32 rx_level; /* Indicates the idle level of RX */ u8 rx_flag; /* Indicates if rxfifo has received data */ unsigned int *tx_buf; u32 tx_size; u32 tx_idx; struct completion tx_complete; }; void aic_tx_fifo_clear(struct aic_ir *ir) { unsigned int val; val = readl(ir->base + CIR_MCR_REG); val |= CIR_MCR_TXFIFO_CLR; writel(val, ir->base + CIR_MCR_REG); } void aic_tx_int_enable(struct aic_ir *ir) { unsigned int val; val = readl(ir->base + CIR_INTEN_REG); val |= CIR_INTEN_TXB_AVL_EN | CIR_INTEN_TXEND_EN | CIR_INTEN_TX_UNF_EN; writel(val, ir->base + CIR_INTEN_REG); } void aic_tx_int_disable(struct aic_ir *ir) { unsigned int val; val = readl(ir->base + CIR_INTEN_REG); val &= ~(CIR_INTEN_TXB_AVL_EN | CIR_INTEN_TXEND_EN | CIR_INTEN_TX_UNF_EN); writel(val, ir->base + CIR_INTEN_REG); } void aic_cir_tx_handle(struct aic_ir *ir) { unsigned int tx_status, tx_dlen, free_space, i; unsigned int rlc_max, val, duration; tx_status = readl(ir->base + CIR_TXSTAT_REG); tx_dlen = tx_status & CIR_TXSTAT_TXFIFO_DLEN_MASK; if (tx_status & CIR_TXSTAT_TXFIFO_EMPTY) free_space = CIR_TXFIFO_SIZE; else free_space = CIR_TXFIFO_SIZE - tx_dlen - 1; rlc_max = 128 * ir->rc->tx_resolution; /* * For each level toggle, generate RLC code according to * tx_buf[i] value */ for (i = ir->tx_idx; (i < ir->tx_size) && free_space; i++) { duration = ir->tx_buf[i]; if (DIV_ROUND_UP(duration, rlc_max) > free_space) break; while (duration > rlc_max) { val = (((i & 1) == 0) << 7) | 0x7F; writel(val, ir->base + CIR_TXFIFO_REG); free_space--; duration -= rlc_max; } duration = duration / ir->rc->tx_resolution; val = (((i & 1) == 0) << 7) | duration; writel(val, ir->base + CIR_TXFIFO_REG); free_space--; } ir->tx_idx = i; } static irqreturn_t aic_ir_irq(int irqno, void *dev_id) { unsigned int int_status; unsigned int rx_status; unsigned int data; unsigned int i, count; struct aic_ir *ir = dev_id; struct ir_raw_event rawir = {}; spin_lock(&ir->ir_lock); int_status = readl(ir->base + CIR_INTR_REG); rx_status = readl(ir->base + CIR_RXSTAT_REG); /* clear all pending status */ writel(0xFF, ir->base + CIR_INTR_REG); /* Handle Receive data */ if (int_status & (CIR_INTR_RXB_AVL_INT | CIR_INTR_RX_END_INT)) { /* Get the number of data in RXFIFO */ count = rx_status & 0x3F; /* Confirm if RXFIFO has data */ if (!(rx_status & (0x1 << 8))) { for (i = 0; i < count; i++) { data = readl(ir->base + CIR_RXFIFO_REG); rawir.pulse = ((data & 0x80) != 0) ^ ir->rx_level; rawir.duration = ((data & 0x7F) + 1) * ir->rc->rx_resolution; ir_raw_event_store_with_filter(ir->rc, &rawir); } ir->rx_flag = 1; } } if (int_status & CIR_INTR_RX_OVF_INT) { ir_raw_event_reset(ir->rc); ir->rx_flag = 0; } else if ((int_status & CIR_INTR_RX_END_INT) && ir->rx_flag) { ir_raw_event_set_idle(ir->rc, true); ir_raw_event_handle(ir->rc); ir->rx_flag = 0; } if (int_status & CIR_INTR_TXB_AVL_INT) aic_cir_tx_handle(ir); if (int_status & CIR_INTR_TXEND_INT) { aic_tx_int_disable(ir); complete(&ir->tx_complete); } spin_unlock(&ir->ir_lock); return IRQ_HANDLED; } static int aic_set_rx_carrier_range(struct rc_dev *rcdev, u32 min, u32 max) { unsigned int clk_div = 0; unsigned int mod_clk = 0; struct aic_ir *ir = rcdev->priv; /* Get CIR module clock */ mod_clk = clk_get_rate(ir->clk); /* Calculate clock divider */ clk_div = DIV_ROUND_UP(mod_clk, max); dev_dbg(&rcdev->dev, "max: %d, clk_div: %d\n", max, clk_div); writel(clk_div - 1, ir->base + CIR_RXCLK_REG); /* Re-calculate rx-resolution and tx-resolution */ rcdev->rx_resolution = (US_PER_SEC / max); dev_dbg(&rcdev->dev, "rx_resolution: %d\n", rcdev->rx_resolution); return 0; } static int aic_set_tx_duty_cycle(struct rc_dev *rcdev, u32 duty_cycle) { struct aic_ir *ir = rcdev->priv; if (duty_cycle < 1 || duty_cycle > 99) return -EINVAL; ir->tx_duty = duty_cycle; return 0; } static int aic_set_tx_carrier(struct rc_dev *rcdev, u32 carrier) { unsigned int val = 0; unsigned int clk_div = 0; unsigned int carrier_high; unsigned int carrier_low; unsigned int mod_clk = 0; struct aic_ir *ir = rcdev->priv; /* Get CIR module clock */ mod_clk = clk_get_rate(ir->clk); /* Calculate clock divider */ clk_div = DIV_ROUND_UP(mod_clk, carrier); carrier_high = mod_clk / carrier * ir->tx_duty / 100; carrier_low = clk_div - carrier_high; val = (carrier_high << 16) | carrier_low; writel(val, ir->base + CIR_CARR_CFG_REG); rcdev->tx_resolution = (US_PER_SEC / carrier); return 0; } static int aic_tx_ir(struct rc_dev *rcdev, unsigned int *txbuf, unsigned int count) { ssize_t ret = 0; unsigned long timeout; unsigned int val; struct aic_ir *ir = rcdev->priv; ir->tx_size = count; ir->tx_idx = 0; ir->tx_buf = txbuf; aic_tx_fifo_clear(ir); aic_cir_tx_handle(ir); /* TX Interrupt Enable */ aic_tx_int_enable(ir); /* Start to transfer */ val = readl(ir->base + CIR_MCR_REG); val |= CIR_MCR_TX_START; writel(val, ir->base + CIR_MCR_REG); timeout = wait_for_completion_timeout(&ir->tx_complete, msecs_to_jiffies(2000)); if (!timeout) ret = -EIO; return ret; } static int aic_cir_probe(struct platform_device *pdev) { struct aic_ir *ir; struct resource *res; struct device *dev = &pdev->dev; struct device_node *dn = dev->of_node; unsigned int val = 0; unsigned int mod_clk = 0; unsigned int clk_div = 0; unsigned int carrier_high = 0; unsigned int carrier_low = 0; int ret = 0; ir = devm_kzalloc(dev, sizeof(struct aic_ir), GFP_KERNEL); if (!ir) return -ENOMEM; spin_lock_init(&ir->ir_lock); ir->clk = devm_clk_get(dev, NULL); if (IS_ERR(ir->clk)) { dev_err(dev, "Failed to get CIR clock\n"); return PTR_ERR(ir->clk); } if (clk_prepare_enable(ir->clk)) { dev_err(dev, "try to enable CIR clock failed\n"); return -EINVAL; } ir->rst = devm_reset_control_get_exclusive(dev, NULL); if (IS_ERR(ir->rst)) return PTR_ERR(ir->rst); ret = reset_control_deassert(ir->rst); if (ret) goto exit_clk_disable; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ir->base = devm_ioremap_resource(dev, res); if (IS_ERR(ir->base)) { ret = PTR_ERR(ir->base); goto exit_reset_assert; } ir->rc = rc_allocate_device(RC_DRIVER_IR_RAW); if (!ir->rc) { dev_err(dev, "Failed to allocate device\n"); ret = -ENOMEM; goto exit_reset_assert; } ir->tx_duty = 33; /* Carrier duty: 33% */ ir->rc->priv = ir; ir->rc->device_name = AIC_IR_DEV; ir->rc->input_phys = "aic-ir/input0"; ir->rc->input_id.bustype = BUS_HOST; ir->rc->input_id.vendor = 0x0001; ir->rc->input_id.product = 0x0001; ir->rc->input_id.version = 0x0100; ir->map_name = of_get_property(dn, "linux,rc-map-name", NULL); ir->rc->map_name = ir->map_name ? : RC_MAP_EMPTY; ir->rc->dev.parent = dev; ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; /* Frequency after IR internal divider with sample period in ns */ ir->rc->rx_resolution = (US_PER_SEC / DEFAULT_FREQ); ir->rc->tx_resolution = (US_PER_SEC / DEFAULT_FREQ); ir->rc->min_timeout = 1; ir->rc->max_timeout = 10 * IR_DEFAULT_TIMEOUT; ir->rc->driver_name = AIC_IR_DEV; ir->rc->s_rx_carrier_range = aic_set_rx_carrier_range; ir->rc->s_tx_carrier = aic_set_tx_carrier; ir->rc->s_tx_duty_cycle = aic_set_tx_duty_cycle; ir->rc->tx_ir = aic_tx_ir; ret = rc_register_device(ir->rc); if (ret) { dev_err(dev, "Failed to register rc device\n"); goto exit_free_dev; } platform_set_drvdata(pdev, ir); if (of_property_read_u32(dn, "rx-level", &ir->rx_level)) { dev_err(dev, "failed to get rx-level property\n"); return -EINVAL; } if (of_property_read_bool(dn, "tx-invert")) { val = readl(ir->base + CIR_TX_CFG_REG); val |= CIR_TX_CFG_TX_INVERT; writel(val, ir->base + CIR_TX_CFG_REG); } /* Set RX CFG */ val = ir->rx_level << 1; if (of_property_read_bool(dn, "rx-invert")) val |= CIR_RX_CFG_RX_INVERT; if (of_device_is_compatible(dn, "artinchip,aic-cir-v1.0")) val |= CIR_RX_CFG_NOISE_LEVEL_V1(RX_NOISE_LEVEL_V1); else val |= CIR_RX_CFG_NOISE_LEVEL(RX_NOISE_LEVEL); writel(val, ir->base + CIR_RX_CFG_REG); /* Set RX active and idle threshold */ writel((CIR_RX_THRES_ACTIVE_LEVEL(0x10) | CIR_RX_THRES_IDLE_LEVEL(0x400)), ir->base + CIR_RX_THRES_REG); /* Set RX default sample clock, Default protocol is NEC */ mod_clk = clk_get_rate(ir->clk); clk_div = DIV_ROUND_UP(mod_clk, DEFAULT_FREQ); writel(clk_div - 1, ir->base + CIR_RXCLK_REG); /* Configure carrier, Default protocol is NEC, 38000 */ carrier_high = mod_clk / DEFAULT_FREQ * ir->tx_duty /100; carrier_low = clk_div - carrier_high; writel((carrier_high << 16) | carrier_low, ir->base + CIR_CARR_CFG_REG); /* Flush TX and RX FIFO */ writel((CIR_MCR_RXFIFO_CLR | CIR_MCR_TXFIFO_CLR), ir->base + CIR_MCR_REG); /* Set TX and RX FIFO level */ writel((CIR_INTEN_TXB_EMPTY_LEVEL(0x40) | CIR_INTEN_RXB_AVL_LEVEL(0x20)), ir->base + CIR_INTEN_REG); /* Clear pending interrupt flags */ writel(0xff, ir->base + CIR_INTR_REG); /* Interrupt Enable */ val = readl(ir->base + CIR_INTEN_REG) & (~0xFF); val |= 0x7; writel(val, ir->base + CIR_INTEN_REG); /* IRQ */ ir->irq = platform_get_irq(pdev, 0); if (ir->irq < 0) { ret = ir->irq; goto exit_free_dev; } ret = devm_request_irq(dev, ir->irq, aic_ir_irq, 0, AIC_IR_DEV, ir); if (ret) { dev_err(dev, "Failed to request irq\n"); goto exit_free_dev; } init_completion(&ir->tx_complete); /* Enable Receiver and Transmitter */ val = readl(ir->base + CIR_MCR_REG); val |= (0x3); writel(val, ir->base + CIR_MCR_REG); dev_info(dev, "Initialized AIC IR driver\n"); return 0; exit_free_dev: rc_free_device(ir->rc); exit_reset_assert: reset_control_assert(ir->rst); exit_clk_disable: clk_disable_unprepare(ir->clk); return ret; } static int aic_cir_remove(struct platform_device *pdev) { unsigned long flags; struct aic_ir *ir = platform_get_drvdata(pdev); clk_disable_unprepare(ir->clk); reset_control_assert(ir->rst); spin_lock_irqsave(&ir->ir_lock, flags); /* Disable IR */ writel(0, ir->base + CIR_MCR_REG); spin_unlock_irqrestore(&ir->ir_lock, flags); rc_unregister_device(ir->rc); return 0; } static const struct of_device_id aic_cir_match[] = { { .compatible = "artinchip,aic-cir-v0.1", }, { .compatible = "artinchip,aic-cir-v1.0", }, {} }; MODULE_DEVICE_TABLE(of, aic_cir_match); static struct platform_driver aic_cir_driver = { .probe = aic_cir_probe, .remove = aic_cir_remove, .driver = { .name = AIC_IR_DEV, .of_match_table = aic_cir_match, }, }; module_platform_driver(aic_cir_driver); MODULE_DESCRIPTION("ArtInChip CIR controller driver"); MODULE_AUTHOR("dwj "); MODULE_LICENSE("GPL");