Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  Copyright (C) 2014 STMicroelectronics – All Rights Reserved
0004  *
0005  *  Author: Lee Jones <lee.jones@linaro.org>
0006  *
0007  *  This is a re-write of Christophe Kerello's PMU driver.
0008  */
0009 
0010 #include <dt-bindings/interrupt-controller/irq-st.h>
0011 #include <linux/err.h>
0012 #include <linux/mfd/syscon.h>
0013 #include <linux/of_device.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/regmap.h>
0016 #include <linux/slab.h>
0017 
0018 #define STIH415_SYSCFG_642      0x0a8
0019 #define STIH416_SYSCFG_7543     0x87c
0020 #define STIH407_SYSCFG_5102     0x198
0021 #define STID127_SYSCFG_734      0x088
0022 
0023 #define ST_A9_IRQ_MASK          0x001FFFFF
0024 #define ST_A9_IRQ_MAX_CHANS     2
0025 
0026 #define ST_A9_IRQ_EN_CTI_0      BIT(0)
0027 #define ST_A9_IRQ_EN_CTI_1      BIT(1)
0028 #define ST_A9_IRQ_EN_PMU_0      BIT(2)
0029 #define ST_A9_IRQ_EN_PMU_1      BIT(3)
0030 #define ST_A9_IRQ_EN_PL310_L2       BIT(4)
0031 #define ST_A9_IRQ_EN_EXT_0      BIT(5)
0032 #define ST_A9_IRQ_EN_EXT_1      BIT(6)
0033 #define ST_A9_IRQ_EN_EXT_2      BIT(7)
0034 
0035 #define ST_A9_FIQ_N_SEL(dev, chan)  (dev << (8  + (chan * 3)))
0036 #define ST_A9_IRQ_N_SEL(dev, chan)  (dev << (14 + (chan * 3)))
0037 #define ST_A9_EXTIRQ_INV_SEL(dev)   (dev << 20)
0038 
0039 struct st_irq_syscfg {
0040     struct regmap *regmap;
0041     unsigned int syscfg;
0042     unsigned int config;
0043     bool ext_inverted;
0044 };
0045 
0046 static const struct of_device_id st_irq_syscfg_match[] = {
0047     {
0048         .compatible = "st,stih415-irq-syscfg",
0049         .data = (void *)STIH415_SYSCFG_642,
0050     },
0051     {
0052         .compatible = "st,stih416-irq-syscfg",
0053         .data = (void *)STIH416_SYSCFG_7543,
0054     },
0055     {
0056         .compatible = "st,stih407-irq-syscfg",
0057         .data = (void *)STIH407_SYSCFG_5102,
0058     },
0059     {
0060         .compatible = "st,stid127-irq-syscfg",
0061         .data = (void *)STID127_SYSCFG_734,
0062     },
0063     {}
0064 };
0065 
0066 static int st_irq_xlate(struct platform_device *pdev,
0067             int device, int channel, bool irq)
0068 {
0069     struct st_irq_syscfg *ddata = dev_get_drvdata(&pdev->dev);
0070 
0071     /* Set the device enable bit. */
0072     switch (device) {
0073     case ST_IRQ_SYSCFG_EXT_0:
0074         ddata->config |= ST_A9_IRQ_EN_EXT_0;
0075         break;
0076     case ST_IRQ_SYSCFG_EXT_1:
0077         ddata->config |= ST_A9_IRQ_EN_EXT_1;
0078         break;
0079     case ST_IRQ_SYSCFG_EXT_2:
0080         ddata->config |= ST_A9_IRQ_EN_EXT_2;
0081         break;
0082     case ST_IRQ_SYSCFG_CTI_0:
0083         ddata->config |= ST_A9_IRQ_EN_CTI_0;
0084         break;
0085     case ST_IRQ_SYSCFG_CTI_1:
0086         ddata->config |= ST_A9_IRQ_EN_CTI_1;
0087         break;
0088     case ST_IRQ_SYSCFG_PMU_0:
0089         ddata->config |= ST_A9_IRQ_EN_PMU_0;
0090         break;
0091     case ST_IRQ_SYSCFG_PMU_1:
0092         ddata->config |= ST_A9_IRQ_EN_PMU_1;
0093         break;
0094     case ST_IRQ_SYSCFG_pl310_L2:
0095         ddata->config |= ST_A9_IRQ_EN_PL310_L2;
0096         break;
0097     case ST_IRQ_SYSCFG_DISABLED:
0098         return 0;
0099     default:
0100         dev_err(&pdev->dev, "Unrecognised device %d\n", device);
0101         return -EINVAL;
0102     }
0103 
0104     /* Select IRQ/FIQ channel for device. */
0105     ddata->config |= irq ?
0106         ST_A9_IRQ_N_SEL(device, channel) :
0107         ST_A9_FIQ_N_SEL(device, channel);
0108 
0109     return 0;
0110 }
0111 
0112 static int st_irq_syscfg_enable(struct platform_device *pdev)
0113 {
0114     struct device_node *np = pdev->dev.of_node;
0115     struct st_irq_syscfg *ddata = dev_get_drvdata(&pdev->dev);
0116     int channels, ret, i;
0117     u32 device, invert;
0118 
0119     channels = of_property_count_u32_elems(np, "st,irq-device");
0120     if (channels != ST_A9_IRQ_MAX_CHANS) {
0121         dev_err(&pdev->dev, "st,enable-irq-device must have 2 elems\n");
0122         return -EINVAL;
0123     }
0124 
0125     channels = of_property_count_u32_elems(np, "st,fiq-device");
0126     if (channels != ST_A9_IRQ_MAX_CHANS) {
0127         dev_err(&pdev->dev, "st,enable-fiq-device must have 2 elems\n");
0128         return -EINVAL;
0129     }
0130 
0131     for (i = 0; i < ST_A9_IRQ_MAX_CHANS; i++) {
0132         of_property_read_u32_index(np, "st,irq-device", i, &device);
0133 
0134         ret = st_irq_xlate(pdev, device, i, true);
0135         if (ret)
0136             return ret;
0137 
0138         of_property_read_u32_index(np, "st,fiq-device", i, &device);
0139 
0140         ret = st_irq_xlate(pdev, device, i, false);
0141         if (ret)
0142             return ret;
0143     }
0144 
0145     /* External IRQs may be inverted. */
0146     of_property_read_u32(np, "st,invert-ext", &invert);
0147     ddata->config |= ST_A9_EXTIRQ_INV_SEL(invert);
0148 
0149     return regmap_update_bits(ddata->regmap, ddata->syscfg,
0150                   ST_A9_IRQ_MASK, ddata->config);
0151 }
0152 
0153 static int st_irq_syscfg_probe(struct platform_device *pdev)
0154 {
0155     struct device_node *np = pdev->dev.of_node;
0156     const struct of_device_id *match;
0157     struct st_irq_syscfg *ddata;
0158 
0159     ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
0160     if (!ddata)
0161         return -ENOMEM;
0162 
0163     match = of_match_device(st_irq_syscfg_match, &pdev->dev);
0164     if (!match)
0165         return -ENODEV;
0166 
0167     ddata->syscfg = (unsigned int)match->data;
0168 
0169     ddata->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
0170     if (IS_ERR(ddata->regmap)) {
0171         dev_err(&pdev->dev, "syscfg phandle missing\n");
0172         return PTR_ERR(ddata->regmap);
0173     }
0174 
0175     dev_set_drvdata(&pdev->dev, ddata);
0176 
0177     return st_irq_syscfg_enable(pdev);
0178 }
0179 
0180 static int __maybe_unused st_irq_syscfg_resume(struct device *dev)
0181 {
0182     struct st_irq_syscfg *ddata = dev_get_drvdata(dev);
0183 
0184     return regmap_update_bits(ddata->regmap, ddata->syscfg,
0185                   ST_A9_IRQ_MASK, ddata->config);
0186 }
0187 
0188 static SIMPLE_DEV_PM_OPS(st_irq_syscfg_pm_ops, NULL, st_irq_syscfg_resume);
0189 
0190 static struct platform_driver st_irq_syscfg_driver = {
0191     .driver = {
0192         .name = "st_irq_syscfg",
0193         .pm = &st_irq_syscfg_pm_ops,
0194         .of_match_table = st_irq_syscfg_match,
0195     },
0196     .probe = st_irq_syscfg_probe,
0197 };
0198 
0199 static int __init st_irq_syscfg_init(void)
0200 {
0201     return platform_driver_register(&st_irq_syscfg_driver);
0202 }
0203 core_initcall(st_irq_syscfg_init);