1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Driver for Allwinner sunXi IR controller 4 * 5 * Copyright (C) 2014 Alexsey Shestacov <wingrime@linux-sunxi.org> 6 * Copyright (C) 2014 Alexander Bersenev <bay@hackerdom.ru> 7 * 8 * Based on sun5i-ir.c: 9 * Copyright (C) 2007-2012 Daniel Wang 10 * Allwinner Technology Co., Ltd. <www.allwinnertech.com> 11 */ 12 13#include <linux/clk.h> 14#include <linux/interrupt.h> 15#include <linux/module.h> 16#include <linux/of_platform.h> 17#include <linux/reset.h> 18#include <media/rc-core.h> 19 20#define SUNXI_IR_DEV "sunxi-ir" 21 22/* Registers */ 23/* IR Control */ 24#define SUNXI_IR_CTL_REG 0x00 25/* Global Enable */ 26#define REG_CTL_GEN BIT(0) 27/* RX block enable */ 28#define REG_CTL_RXEN BIT(1) 29/* CIR mode */ 30#define REG_CTL_MD (BIT(4) | BIT(5)) 31 32/* Rx Config */ 33#define SUNXI_IR_RXCTL_REG 0x10 34/* Pulse Polarity Invert flag */ 35#define REG_RXCTL_RPPI BIT(2) 36 37/* Rx Data */ 38#define SUNXI_IR_RXFIFO_REG 0x20 39 40/* Rx Interrupt Enable */ 41#define SUNXI_IR_RXINT_REG 0x2C 42/* Rx FIFO Overflow Interrupt Enable */ 43#define REG_RXINT_ROI_EN BIT(0) 44/* Rx Packet End Interrupt Enable */ 45#define REG_RXINT_RPEI_EN BIT(1) 46/* Rx FIFO Data Available Interrupt Enable */ 47#define REG_RXINT_RAI_EN BIT(4) 48 49/* Rx FIFO available byte level */ 50#define REG_RXINT_RAL(val) ((val) << 8) 51 52/* Rx Interrupt Status */ 53#define SUNXI_IR_RXSTA_REG 0x30 54/* Rx FIFO Overflow */ 55#define REG_RXSTA_ROI REG_RXINT_ROI_EN 56/* Rx Packet End */ 57#define REG_RXSTA_RPE REG_RXINT_RPEI_EN 58/* Rx FIFO Data Available */ 59#define REG_RXSTA_RA REG_RXINT_RAI_EN 60/* RX FIFO Get Available Counter */ 61#define REG_RXSTA_GET_AC(val) (((val) >> 8) & (ir->fifo_size * 2 - 1)) 62/* Clear all interrupt status value */ 63#define REG_RXSTA_CLEARALL 0xff 64 65/* IR Sample Config */ 66#define SUNXI_IR_CIR_REG 0x34 67/* CIR_REG register noise threshold */ 68#define REG_CIR_NTHR(val) (((val) << 2) & (GENMASK(7, 2))) 69/* CIR_REG register idle threshold */ 70#define REG_CIR_ITHR(val) (((val) << 8) & (GENMASK(15, 8))) 71 72/* Required frequency for IR0 or IR1 clock in CIR mode (default) */ 73#define SUNXI_IR_BASE_CLK 8000000 74/* Noise threshold in samples */ 75#define SUNXI_IR_RXNOISE 1 76/* Idle Threshold in samples */ 77#define SUNXI_IR_RXIDLE 20 78/* Time after which device stops sending data in ms */ 79#define SUNXI_IR_TIMEOUT 120 80 81/** 82 * struct sunxi_ir_quirks - Differences between SoC variants. 83 * 84 * @has_reset: SoC needs reset deasserted. 85 * @fifo_size: size of the fifo. 86 */ 87struct sunxi_ir_quirks { 88 bool has_reset; 89 int fifo_size; 90}; 91 92struct sunxi_ir { 93 spinlock_t ir_lock; 94 struct rc_dev *rc; 95 void __iomem *base; 96 int irq; 97 int fifo_size; 98 struct clk *clk; 99 struct clk *apb_clk; 100 struct reset_control *rst; 101 const char *map_name; 102}; 103 104static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id) 105{ 106 unsigned long status; 107 unsigned char dt; 108 unsigned int cnt, rc; 109 struct sunxi_ir *ir = dev_id; 110 struct ir_raw_event rawir = {}; 111 112 spin_lock(&ir->ir_lock); 113 114 status = readl(ir->base + SUNXI_IR_RXSTA_REG); 115 116 /* clean all pending statuses */ 117 writel(status | REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG); 118 119 if (status & (REG_RXSTA_RA | REG_RXSTA_RPE)) { 120 /* How many messages in fifo */ 121 rc = REG_RXSTA_GET_AC(status); 122 /* Sanity check */ 123 rc = rc > ir->fifo_size ? ir->fifo_size : rc; 124 /* If we have data */ 125 for (cnt = 0; cnt < rc; cnt++) { 126 /* for each bit in fifo */ 127 dt = readb(ir->base + SUNXI_IR_RXFIFO_REG); 128 rawir.pulse = (dt & 0x80) != 0; 129 rawir.duration = ((dt & 0x7f) + 1) * 130 ir->rc->rx_resolution; 131 ir_raw_event_store_with_filter(ir->rc, &rawir); 132 } 133 } 134 135 if (status & REG_RXSTA_ROI) { 136 ir_raw_event_reset(ir->rc); 137 } else if (status & REG_RXSTA_RPE) { 138 ir_raw_event_set_idle(ir->rc, true); 139 ir_raw_event_handle(ir->rc); 140 } else { 141 ir_raw_event_handle(ir->rc); 142 } 143 144 spin_unlock(&ir->ir_lock); 145 146 return IRQ_HANDLED; 147} 148 149static int sunxi_ir_probe(struct platform_device *pdev) 150{ 151 int ret = 0; 152 unsigned long tmp = 0; 153 154 struct device *dev = &pdev->dev; 155 struct device_node *dn = dev->of_node; 156 const struct sunxi_ir_quirks *quirks; 157 struct resource *res; 158 struct sunxi_ir *ir; 159 u32 b_clk_freq = SUNXI_IR_BASE_CLK; 160 161 ir = devm_kzalloc(dev, sizeof(struct sunxi_ir), GFP_KERNEL); 162 if (!ir) 163 return -ENOMEM; 164 165 quirks = of_device_get_match_data(&pdev->dev); 166 if (!quirks) { 167 dev_err(&pdev->dev, "Failed to determine the quirks to use\n"); 168 return -ENODEV; 169 } 170 171 spin_lock_init(&ir->ir_lock); 172 173 ir->fifo_size = quirks->fifo_size; 174 175 /* Clock */ 176 ir->apb_clk = devm_clk_get(dev, "apb"); 177 if (IS_ERR(ir->apb_clk)) { 178 dev_err(dev, "failed to get a apb clock.\n"); 179 return PTR_ERR(ir->apb_clk); 180 } 181 ir->clk = devm_clk_get(dev, "ir"); 182 if (IS_ERR(ir->clk)) { 183 dev_err(dev, "failed to get a ir clock.\n"); 184 return PTR_ERR(ir->clk); 185 } 186 187 /* Base clock frequency (optional) */ 188 of_property_read_u32(dn, "clock-frequency", &b_clk_freq); 189 190 /* Reset */ 191 if (quirks->has_reset) { 192 ir->rst = devm_reset_control_get_exclusive(dev, NULL); 193 if (IS_ERR(ir->rst)) 194 return PTR_ERR(ir->rst); 195 ret = reset_control_deassert(ir->rst); 196 if (ret) 197 return ret; 198 } 199 200 ret = clk_set_rate(ir->clk, b_clk_freq); 201 if (ret) { 202 dev_err(dev, "set ir base clock failed!\n"); 203 goto exit_reset_assert; 204 } 205 dev_dbg(dev, "set base clock frequency to %d Hz.\n", b_clk_freq); 206 207 if (clk_prepare_enable(ir->apb_clk)) { 208 dev_err(dev, "try to enable apb_ir_clk failed\n"); 209 ret = -EINVAL; 210 goto exit_reset_assert; 211 } 212 213 if (clk_prepare_enable(ir->clk)) { 214 dev_err(dev, "try to enable ir_clk failed\n"); 215 ret = -EINVAL; 216 goto exit_clkdisable_apb_clk; 217 } 218 219 /* IO */ 220 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 221 ir->base = devm_ioremap_resource(dev, res); 222 if (IS_ERR(ir->base)) { 223 ret = PTR_ERR(ir->base); 224 goto exit_clkdisable_clk; 225 } 226 227 ir->rc = rc_allocate_device(RC_DRIVER_IR_RAW); 228 if (!ir->rc) { 229 dev_err(dev, "failed to allocate device\n"); 230 ret = -ENOMEM; 231 goto exit_clkdisable_clk; 232 } 233 234 ir->rc->priv = ir; 235 ir->rc->device_name = SUNXI_IR_DEV; 236 ir->rc->input_phys = "sunxi-ir/input0"; 237 ir->rc->input_id.bustype = BUS_HOST; 238 ir->rc->input_id.vendor = 0x0001; 239 ir->rc->input_id.product = 0x0001; 240 ir->rc->input_id.version = 0x0100; 241 ir->map_name = of_get_property(dn, "linux,rc-map-name", NULL); 242 ir->rc->map_name = ir->map_name ?: RC_MAP_EMPTY; 243 ir->rc->dev.parent = dev; 244 ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; 245 /* Frequency after IR internal divider with sample period in ns */ 246 ir->rc->rx_resolution = (USEC_PER_SEC / (b_clk_freq / 64)); 247 ir->rc->timeout = MS_TO_US(SUNXI_IR_TIMEOUT); 248 ir->rc->driver_name = SUNXI_IR_DEV; 249 250 ret = rc_register_device(ir->rc); 251 if (ret) { 252 dev_err(dev, "failed to register rc device\n"); 253 goto exit_free_dev; 254 } 255 256 platform_set_drvdata(pdev, ir); 257 258 /* IRQ */ 259 ir->irq = platform_get_irq(pdev, 0); 260 if (ir->irq < 0) { 261 ret = ir->irq; 262 goto exit_free_dev; 263 } 264 265 ret = devm_request_irq(dev, ir->irq, sunxi_ir_irq, 0, SUNXI_IR_DEV, ir); 266 if (ret) { 267 dev_err(dev, "failed request irq\n"); 268 goto exit_free_dev; 269 } 270 271 /* Enable CIR Mode */ 272 writel(REG_CTL_MD, ir->base+SUNXI_IR_CTL_REG); 273 274 /* Set noise threshold and idle threshold */ 275 writel(REG_CIR_NTHR(SUNXI_IR_RXNOISE)|REG_CIR_ITHR(SUNXI_IR_RXIDLE), 276 ir->base + SUNXI_IR_CIR_REG); 277 278 /* Invert Input Signal */ 279 writel(REG_RXCTL_RPPI, ir->base + SUNXI_IR_RXCTL_REG); 280 281 /* Clear All Rx Interrupt Status */ 282 writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG); 283 284 /* 285 * Enable IRQ on overflow, packet end, FIFO available with trigger 286 * level 287 */ 288 writel(REG_RXINT_ROI_EN | REG_RXINT_RPEI_EN | 289 REG_RXINT_RAI_EN | REG_RXINT_RAL(ir->fifo_size / 2 - 1), 290 ir->base + SUNXI_IR_RXINT_REG); 291 292 /* Enable IR Module */ 293 tmp = readl(ir->base + SUNXI_IR_CTL_REG); 294 writel(tmp | REG_CTL_GEN | REG_CTL_RXEN, ir->base + SUNXI_IR_CTL_REG); 295 296 dev_info(dev, "initialized sunXi IR driver\n"); 297 return 0; 298 299exit_free_dev: 300 rc_free_device(ir->rc); 301exit_clkdisable_clk: 302 clk_disable_unprepare(ir->clk); 303exit_clkdisable_apb_clk: 304 clk_disable_unprepare(ir->apb_clk); 305exit_reset_assert: 306 reset_control_assert(ir->rst); 307 308 return ret; 309} 310 311static int sunxi_ir_remove(struct platform_device *pdev) 312{ 313 unsigned long flags; 314 struct sunxi_ir *ir = platform_get_drvdata(pdev); 315 316 clk_disable_unprepare(ir->clk); 317 clk_disable_unprepare(ir->apb_clk); 318 reset_control_assert(ir->rst); 319 320 spin_lock_irqsave(&ir->ir_lock, flags); 321 /* disable IR IRQ */ 322 writel(0, ir->base + SUNXI_IR_RXINT_REG); 323 /* clear All Rx Interrupt Status */ 324 writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG); 325 /* disable IR */ 326 writel(0, ir->base + SUNXI_IR_CTL_REG); 327 spin_unlock_irqrestore(&ir->ir_lock, flags); 328 329 rc_unregister_device(ir->rc); 330 return 0; 331} 332 333static const struct sunxi_ir_quirks sun4i_a10_ir_quirks = { 334 .has_reset = false, 335 .fifo_size = 16, 336}; 337 338static const struct sunxi_ir_quirks sun5i_a13_ir_quirks = { 339 .has_reset = false, 340 .fifo_size = 64, 341}; 342 343static const struct sunxi_ir_quirks sun6i_a31_ir_quirks = { 344 .has_reset = true, 345 .fifo_size = 64, 346}; 347 348static const struct of_device_id sunxi_ir_match[] = { 349 { 350 .compatible = "allwinner,sun4i-a10-ir", 351 .data = &sun4i_a10_ir_quirks, 352 }, 353 { 354 .compatible = "allwinner,sun5i-a13-ir", 355 .data = &sun5i_a13_ir_quirks, 356 }, 357 { 358 .compatible = "allwinner,sun6i-a31-ir", 359 .data = &sun6i_a31_ir_quirks, 360 }, 361 {} 362}; 363MODULE_DEVICE_TABLE(of, sunxi_ir_match); 364 365static struct platform_driver sunxi_ir_driver = { 366 .probe = sunxi_ir_probe, 367 .remove = sunxi_ir_remove, 368 .driver = { 369 .name = SUNXI_IR_DEV, 370 .of_match_table = sunxi_ir_match, 371 }, 372}; 373 374module_platform_driver(sunxi_ir_driver); 375 376MODULE_DESCRIPTION("Allwinner sunXi IR controller driver"); 377MODULE_AUTHOR("Alexsey Shestacov <wingrime@linux-sunxi.org>"); 378MODULE_LICENSE("GPL"); 379