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