| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (c) 2014 Linaro Ltd. |
|---|
| 3 | 4 | * Copyright (c) 2014 Hisilicon Limited. |
|---|
| 4 | | - * |
|---|
| 5 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 6 | | - * under the terms and conditions of the GNU General Public License, |
|---|
| 7 | | - * version 2, as published by the Free Software Foundation. |
|---|
| 8 | 5 | */ |
|---|
| 9 | 6 | |
|---|
| 10 | 7 | #include <linux/clk.h> |
|---|
| .. | .. |
|---|
| 40 | 37 | #define INT_CLR_RCV BIT(16) |
|---|
| 41 | 38 | #define INT_CLR_RCVTIMEOUT (BIT(16) | BIT(17)) |
|---|
| 42 | 39 | |
|---|
| 43 | | -#define IR_CLK 0x48 |
|---|
| 44 | 40 | #define IR_CLK_ENABLE BIT(4) |
|---|
| 45 | 41 | #define IR_CLK_RESET BIT(5) |
|---|
| 42 | + |
|---|
| 43 | +/* IR_ENABLE register bits */ |
|---|
| 44 | +#define IR_ENABLE_EN BIT(0) |
|---|
| 45 | +#define IR_ENABLE_EN_EXTRA BIT(8) |
|---|
| 46 | 46 | |
|---|
| 47 | 47 | #define IR_CFG_WIDTH_MASK 0xffff |
|---|
| 48 | 48 | #define IR_CFG_WIDTH_SHIFT 16 |
|---|
| .. | .. |
|---|
| 61 | 61 | |
|---|
| 62 | 62 | #define IR_HIX5HD2_NAME "hix5hd2-ir" |
|---|
| 63 | 63 | |
|---|
| 64 | +/* Need to set extra bit for enabling IR */ |
|---|
| 65 | +#define HIX5HD2_FLAG_EXTRA_ENABLE BIT(0) |
|---|
| 66 | + |
|---|
| 67 | +struct hix5hd2_soc_data { |
|---|
| 68 | + u32 clk_reg; |
|---|
| 69 | + u32 flags; |
|---|
| 70 | +}; |
|---|
| 71 | + |
|---|
| 72 | +static const struct hix5hd2_soc_data hix5hd2_data = { |
|---|
| 73 | + .clk_reg = 0x48, |
|---|
| 74 | +}; |
|---|
| 75 | + |
|---|
| 76 | +static const struct hix5hd2_soc_data hi3796cv300_data = { |
|---|
| 77 | + .clk_reg = 0x60, |
|---|
| 78 | + .flags = HIX5HD2_FLAG_EXTRA_ENABLE, |
|---|
| 79 | +}; |
|---|
| 80 | + |
|---|
| 64 | 81 | struct hix5hd2_ir_priv { |
|---|
| 65 | 82 | int irq; |
|---|
| 66 | 83 | void __iomem *base; |
|---|
| .. | .. |
|---|
| 69 | 86 | struct regmap *regmap; |
|---|
| 70 | 87 | struct clk *clock; |
|---|
| 71 | 88 | unsigned long rate; |
|---|
| 89 | + const struct hix5hd2_soc_data *socdata; |
|---|
| 72 | 90 | }; |
|---|
| 73 | 91 | |
|---|
| 74 | | -static int hix5hd2_ir_enable(struct hix5hd2_ir_priv *dev, bool on) |
|---|
| 92 | +static int hix5hd2_ir_clk_enable(struct hix5hd2_ir_priv *dev, bool on) |
|---|
| 75 | 93 | { |
|---|
| 94 | + u32 clk_reg = dev->socdata->clk_reg; |
|---|
| 76 | 95 | u32 val; |
|---|
| 77 | 96 | int ret = 0; |
|---|
| 78 | 97 | |
|---|
| 79 | 98 | if (dev->regmap) { |
|---|
| 80 | | - regmap_read(dev->regmap, IR_CLK, &val); |
|---|
| 99 | + regmap_read(dev->regmap, clk_reg, &val); |
|---|
| 81 | 100 | if (on) { |
|---|
| 82 | 101 | val &= ~IR_CLK_RESET; |
|---|
| 83 | 102 | val |= IR_CLK_ENABLE; |
|---|
| .. | .. |
|---|
| 85 | 104 | val &= ~IR_CLK_ENABLE; |
|---|
| 86 | 105 | val |= IR_CLK_RESET; |
|---|
| 87 | 106 | } |
|---|
| 88 | | - regmap_write(dev->regmap, IR_CLK, val); |
|---|
| 107 | + regmap_write(dev->regmap, clk_reg, val); |
|---|
| 89 | 108 | } else { |
|---|
| 90 | 109 | if (on) |
|---|
| 91 | 110 | ret = clk_prepare_enable(dev->clock); |
|---|
| .. | .. |
|---|
| 95 | 114 | return ret; |
|---|
| 96 | 115 | } |
|---|
| 97 | 116 | |
|---|
| 117 | +static inline void hix5hd2_ir_enable(struct hix5hd2_ir_priv *priv) |
|---|
| 118 | +{ |
|---|
| 119 | + u32 val = IR_ENABLE_EN; |
|---|
| 120 | + |
|---|
| 121 | + if (priv->socdata->flags & HIX5HD2_FLAG_EXTRA_ENABLE) |
|---|
| 122 | + val |= IR_ENABLE_EN_EXTRA; |
|---|
| 123 | + |
|---|
| 124 | + writel_relaxed(val, priv->base + IR_ENABLE); |
|---|
| 125 | +} |
|---|
| 126 | + |
|---|
| 98 | 127 | static int hix5hd2_ir_config(struct hix5hd2_ir_priv *priv) |
|---|
| 99 | 128 | { |
|---|
| 100 | 129 | int timeout = 10000; |
|---|
| 101 | 130 | u32 val, rate; |
|---|
| 102 | 131 | |
|---|
| 103 | | - writel_relaxed(0x01, priv->base + IR_ENABLE); |
|---|
| 132 | + hix5hd2_ir_enable(priv); |
|---|
| 133 | + |
|---|
| 104 | 134 | while (readl_relaxed(priv->base + IR_BUSY)) { |
|---|
| 105 | 135 | if (timeout--) { |
|---|
| 106 | 136 | udelay(1); |
|---|
| .. | .. |
|---|
| 131 | 161 | struct hix5hd2_ir_priv *priv = rdev->priv; |
|---|
| 132 | 162 | int ret; |
|---|
| 133 | 163 | |
|---|
| 134 | | - ret = hix5hd2_ir_enable(priv, true); |
|---|
| 164 | + ret = hix5hd2_ir_clk_enable(priv, true); |
|---|
| 135 | 165 | if (ret) |
|---|
| 136 | 166 | return ret; |
|---|
| 137 | 167 | |
|---|
| 138 | 168 | ret = hix5hd2_ir_config(priv); |
|---|
| 139 | 169 | if (ret) { |
|---|
| 140 | | - hix5hd2_ir_enable(priv, false); |
|---|
| 170 | + hix5hd2_ir_clk_enable(priv, false); |
|---|
| 141 | 171 | return ret; |
|---|
| 142 | 172 | } |
|---|
| 143 | 173 | return 0; |
|---|
| .. | .. |
|---|
| 147 | 177 | { |
|---|
| 148 | 178 | struct hix5hd2_ir_priv *priv = rdev->priv; |
|---|
| 149 | 179 | |
|---|
| 150 | | - hix5hd2_ir_enable(priv, false); |
|---|
| 180 | + hix5hd2_ir_clk_enable(priv, false); |
|---|
| 151 | 181 | } |
|---|
| 152 | 182 | |
|---|
| 153 | 183 | static irqreturn_t hix5hd2_ir_rx_interrupt(int irq, void *data) |
|---|
| .. | .. |
|---|
| 175 | 205 | } |
|---|
| 176 | 206 | |
|---|
| 177 | 207 | if ((irq_sr & INTMS_SYMBRCV) || (irq_sr & INTMS_TIMEOUT)) { |
|---|
| 178 | | - DEFINE_IR_RAW_EVENT(ev); |
|---|
| 208 | + struct ir_raw_event ev = {}; |
|---|
| 179 | 209 | |
|---|
| 180 | 210 | symb_num = readl_relaxed(priv->base + IR_DATAH); |
|---|
| 181 | 211 | for (i = 0; i < symb_num; i++) { |
|---|
| .. | .. |
|---|
| 184 | 214 | data_h = ((symb_val >> 16) & 0xffff) * 10; |
|---|
| 185 | 215 | symb_time = (data_l + data_h) / 10; |
|---|
| 186 | 216 | |
|---|
| 187 | | - ev.duration = US_TO_NS(data_l); |
|---|
| 217 | + ev.duration = data_l; |
|---|
| 188 | 218 | ev.pulse = true; |
|---|
| 189 | 219 | ir_raw_event_store(priv->rdev, &ev); |
|---|
| 190 | 220 | |
|---|
| 191 | 221 | if (symb_time < IR_CFG_SYMBOL_MAXWIDTH) { |
|---|
| 192 | | - ev.duration = US_TO_NS(data_h); |
|---|
| 222 | + ev.duration = data_h; |
|---|
| 193 | 223 | ev.pulse = false; |
|---|
| 194 | 224 | ir_raw_event_store(priv->rdev, &ev); |
|---|
| 195 | 225 | } else { |
|---|
| .. | .. |
|---|
| 208 | 238 | return IRQ_HANDLED; |
|---|
| 209 | 239 | } |
|---|
| 210 | 240 | |
|---|
| 241 | +static const struct of_device_id hix5hd2_ir_table[] = { |
|---|
| 242 | + { .compatible = "hisilicon,hix5hd2-ir", &hix5hd2_data, }, |
|---|
| 243 | + { .compatible = "hisilicon,hi3796cv300-ir", &hi3796cv300_data, }, |
|---|
| 244 | + {}, |
|---|
| 245 | +}; |
|---|
| 246 | +MODULE_DEVICE_TABLE(of, hix5hd2_ir_table); |
|---|
| 247 | + |
|---|
| 211 | 248 | static int hix5hd2_ir_probe(struct platform_device *pdev) |
|---|
| 212 | 249 | { |
|---|
| 213 | 250 | struct rc_dev *rdev; |
|---|
| .. | .. |
|---|
| 215 | 252 | struct resource *res; |
|---|
| 216 | 253 | struct hix5hd2_ir_priv *priv; |
|---|
| 217 | 254 | struct device_node *node = pdev->dev.of_node; |
|---|
| 255 | + const struct of_device_id *of_id; |
|---|
| 218 | 256 | const char *map_name; |
|---|
| 219 | 257 | int ret; |
|---|
| 220 | 258 | |
|---|
| 221 | 259 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
|---|
| 222 | 260 | if (!priv) |
|---|
| 223 | 261 | return -ENOMEM; |
|---|
| 262 | + |
|---|
| 263 | + of_id = of_match_device(hix5hd2_ir_table, dev); |
|---|
| 264 | + if (!of_id) { |
|---|
| 265 | + dev_err(dev, "Unable to initialize IR data\n"); |
|---|
| 266 | + return -ENODEV; |
|---|
| 267 | + } |
|---|
| 268 | + priv->socdata = of_id->data; |
|---|
| 224 | 269 | |
|---|
| 225 | 270 | priv->regmap = syscon_regmap_lookup_by_phandle(node, |
|---|
| 226 | 271 | "hisilicon,power-syscon"); |
|---|
| .. | .. |
|---|
| 235 | 280 | return PTR_ERR(priv->base); |
|---|
| 236 | 281 | |
|---|
| 237 | 282 | priv->irq = platform_get_irq(pdev, 0); |
|---|
| 238 | | - if (priv->irq < 0) { |
|---|
| 239 | | - dev_err(dev, "irq can not get\n"); |
|---|
| 283 | + if (priv->irq < 0) |
|---|
| 240 | 284 | return priv->irq; |
|---|
| 241 | | - } |
|---|
| 242 | 285 | |
|---|
| 243 | 286 | rdev = rc_allocate_device(RC_DRIVER_IR_RAW); |
|---|
| 244 | 287 | if (!rdev) |
|---|
| .. | .. |
|---|
| 268 | 311 | rdev->input_id.vendor = 0x0001; |
|---|
| 269 | 312 | rdev->input_id.product = 0x0001; |
|---|
| 270 | 313 | rdev->input_id.version = 0x0100; |
|---|
| 271 | | - rdev->rx_resolution = US_TO_NS(10); |
|---|
| 272 | | - rdev->timeout = US_TO_NS(IR_CFG_SYMBOL_MAXWIDTH * 10); |
|---|
| 314 | + rdev->rx_resolution = 10; |
|---|
| 315 | + rdev->timeout = IR_CFG_SYMBOL_MAXWIDTH * 10; |
|---|
| 273 | 316 | |
|---|
| 274 | 317 | ret = rc_register_device(rdev); |
|---|
| 275 | 318 | if (ret < 0) |
|---|
| .. | .. |
|---|
| 314 | 357 | struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev); |
|---|
| 315 | 358 | |
|---|
| 316 | 359 | clk_disable_unprepare(priv->clock); |
|---|
| 317 | | - hix5hd2_ir_enable(priv, false); |
|---|
| 360 | + hix5hd2_ir_clk_enable(priv, false); |
|---|
| 318 | 361 | |
|---|
| 319 | 362 | return 0; |
|---|
| 320 | 363 | } |
|---|
| .. | .. |
|---|
| 324 | 367 | struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev); |
|---|
| 325 | 368 | int ret; |
|---|
| 326 | 369 | |
|---|
| 327 | | - ret = hix5hd2_ir_enable(priv, true); |
|---|
| 370 | + ret = hix5hd2_ir_clk_enable(priv, true); |
|---|
| 328 | 371 | if (ret) |
|---|
| 329 | 372 | return ret; |
|---|
| 330 | 373 | |
|---|
| 331 | 374 | ret = clk_prepare_enable(priv->clock); |
|---|
| 332 | 375 | if (ret) { |
|---|
| 333 | | - hix5hd2_ir_enable(priv, false); |
|---|
| 376 | + hix5hd2_ir_clk_enable(priv, false); |
|---|
| 334 | 377 | return ret; |
|---|
| 335 | 378 | } |
|---|
| 336 | 379 | |
|---|
| 337 | | - writel_relaxed(0x01, priv->base + IR_ENABLE); |
|---|
| 380 | + hix5hd2_ir_enable(priv); |
|---|
| 381 | + |
|---|
| 338 | 382 | writel_relaxed(0x00, priv->base + IR_INTM); |
|---|
| 339 | 383 | writel_relaxed(0xff, priv->base + IR_INTC); |
|---|
| 340 | 384 | writel_relaxed(0x01, priv->base + IR_START); |
|---|
| .. | .. |
|---|
| 345 | 389 | |
|---|
| 346 | 390 | static SIMPLE_DEV_PM_OPS(hix5hd2_ir_pm_ops, hix5hd2_ir_suspend, |
|---|
| 347 | 391 | hix5hd2_ir_resume); |
|---|
| 348 | | - |
|---|
| 349 | | -static const struct of_device_id hix5hd2_ir_table[] = { |
|---|
| 350 | | - { .compatible = "hisilicon,hix5hd2-ir", }, |
|---|
| 351 | | - {}, |
|---|
| 352 | | -}; |
|---|
| 353 | | -MODULE_DEVICE_TABLE(of, hix5hd2_ir_table); |
|---|
| 354 | 392 | |
|---|
| 355 | 393 | static struct platform_driver hix5hd2_ir_driver = { |
|---|
| 356 | 394 | .driver = { |
|---|