Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright 2017 Google Inc
0004  *
0005  * Provides a simple driver to control the ASPEED LPC snoop interface which
0006  * allows the BMC to listen on and save the data written by
0007  * the host to an arbitrary LPC I/O port.
0008  *
0009  * Typically used by the BMC to "watch" host boot progress via port
0010  * 0x80 writes made by the BIOS during the boot process.
0011  */
0012 
0013 #include <linux/bitops.h>
0014 #include <linux/clk.h>
0015 #include <linux/interrupt.h>
0016 #include <linux/fs.h>
0017 #include <linux/kfifo.h>
0018 #include <linux/mfd/syscon.h>
0019 #include <linux/miscdevice.h>
0020 #include <linux/module.h>
0021 #include <linux/of.h>
0022 #include <linux/of_device.h>
0023 #include <linux/platform_device.h>
0024 #include <linux/poll.h>
0025 #include <linux/regmap.h>
0026 
0027 #define DEVICE_NAME "aspeed-lpc-snoop"
0028 
0029 #define NUM_SNOOP_CHANNELS 2
0030 #define SNOOP_FIFO_SIZE 2048
0031 
0032 #define HICR5   0x80
0033 #define HICR5_EN_SNP0W      BIT(0)
0034 #define HICR5_ENINT_SNP0W   BIT(1)
0035 #define HICR5_EN_SNP1W      BIT(2)
0036 #define HICR5_ENINT_SNP1W   BIT(3)
0037 #define HICR6   0x84
0038 #define HICR6_STR_SNP0W     BIT(0)
0039 #define HICR6_STR_SNP1W     BIT(1)
0040 #define SNPWADR 0x90
0041 #define SNPWADR_CH0_MASK    GENMASK(15, 0)
0042 #define SNPWADR_CH0_SHIFT   0
0043 #define SNPWADR_CH1_MASK    GENMASK(31, 16)
0044 #define SNPWADR_CH1_SHIFT   16
0045 #define SNPWDR  0x94
0046 #define SNPWDR_CH0_MASK     GENMASK(7, 0)
0047 #define SNPWDR_CH0_SHIFT    0
0048 #define SNPWDR_CH1_MASK     GENMASK(15, 8)
0049 #define SNPWDR_CH1_SHIFT    8
0050 #define HICRB   0x100
0051 #define HICRB_ENSNP0D       BIT(14)
0052 #define HICRB_ENSNP1D       BIT(15)
0053 
0054 struct aspeed_lpc_snoop_model_data {
0055     /* The ast2400 has bits 14 and 15 as reserved, whereas the ast2500
0056      * can use them.
0057      */
0058     unsigned int has_hicrb_ensnp;
0059 };
0060 
0061 struct aspeed_lpc_snoop_channel {
0062     struct kfifo        fifo;
0063     wait_queue_head_t   wq;
0064     struct miscdevice   miscdev;
0065 };
0066 
0067 struct aspeed_lpc_snoop {
0068     struct regmap       *regmap;
0069     int         irq;
0070     struct clk      *clk;
0071     struct aspeed_lpc_snoop_channel chan[NUM_SNOOP_CHANNELS];
0072 };
0073 
0074 static struct aspeed_lpc_snoop_channel *snoop_file_to_chan(struct file *file)
0075 {
0076     return container_of(file->private_data,
0077                 struct aspeed_lpc_snoop_channel,
0078                 miscdev);
0079 }
0080 
0081 static ssize_t snoop_file_read(struct file *file, char __user *buffer,
0082                 size_t count, loff_t *ppos)
0083 {
0084     struct aspeed_lpc_snoop_channel *chan = snoop_file_to_chan(file);
0085     unsigned int copied;
0086     int ret = 0;
0087 
0088     if (kfifo_is_empty(&chan->fifo)) {
0089         if (file->f_flags & O_NONBLOCK)
0090             return -EAGAIN;
0091         ret = wait_event_interruptible(chan->wq,
0092                 !kfifo_is_empty(&chan->fifo));
0093         if (ret == -ERESTARTSYS)
0094             return -EINTR;
0095     }
0096     ret = kfifo_to_user(&chan->fifo, buffer, count, &copied);
0097     if (ret)
0098         return ret;
0099 
0100     return copied;
0101 }
0102 
0103 static __poll_t snoop_file_poll(struct file *file,
0104                     struct poll_table_struct *pt)
0105 {
0106     struct aspeed_lpc_snoop_channel *chan = snoop_file_to_chan(file);
0107 
0108     poll_wait(file, &chan->wq, pt);
0109     return !kfifo_is_empty(&chan->fifo) ? EPOLLIN : 0;
0110 }
0111 
0112 static const struct file_operations snoop_fops = {
0113     .owner  = THIS_MODULE,
0114     .read   = snoop_file_read,
0115     .poll   = snoop_file_poll,
0116     .llseek = noop_llseek,
0117 };
0118 
0119 /* Save a byte to a FIFO and discard the oldest byte if FIFO is full */
0120 static void put_fifo_with_discard(struct aspeed_lpc_snoop_channel *chan, u8 val)
0121 {
0122     if (!kfifo_initialized(&chan->fifo))
0123         return;
0124     if (kfifo_is_full(&chan->fifo))
0125         kfifo_skip(&chan->fifo);
0126     kfifo_put(&chan->fifo, val);
0127     wake_up_interruptible(&chan->wq);
0128 }
0129 
0130 static irqreturn_t aspeed_lpc_snoop_irq(int irq, void *arg)
0131 {
0132     struct aspeed_lpc_snoop *lpc_snoop = arg;
0133     u32 reg, data;
0134 
0135     if (regmap_read(lpc_snoop->regmap, HICR6, &reg))
0136         return IRQ_NONE;
0137 
0138     /* Check if one of the snoop channels is interrupting */
0139     reg &= (HICR6_STR_SNP0W | HICR6_STR_SNP1W);
0140     if (!reg)
0141         return IRQ_NONE;
0142 
0143     /* Ack pending IRQs */
0144     regmap_write(lpc_snoop->regmap, HICR6, reg);
0145 
0146     /* Read and save most recent snoop'ed data byte to FIFO */
0147     regmap_read(lpc_snoop->regmap, SNPWDR, &data);
0148 
0149     if (reg & HICR6_STR_SNP0W) {
0150         u8 val = (data & SNPWDR_CH0_MASK) >> SNPWDR_CH0_SHIFT;
0151 
0152         put_fifo_with_discard(&lpc_snoop->chan[0], val);
0153     }
0154     if (reg & HICR6_STR_SNP1W) {
0155         u8 val = (data & SNPWDR_CH1_MASK) >> SNPWDR_CH1_SHIFT;
0156 
0157         put_fifo_with_discard(&lpc_snoop->chan[1], val);
0158     }
0159 
0160     return IRQ_HANDLED;
0161 }
0162 
0163 static int aspeed_lpc_snoop_config_irq(struct aspeed_lpc_snoop *lpc_snoop,
0164                        struct platform_device *pdev)
0165 {
0166     struct device *dev = &pdev->dev;
0167     int rc;
0168 
0169     lpc_snoop->irq = platform_get_irq(pdev, 0);
0170     if (!lpc_snoop->irq)
0171         return -ENODEV;
0172 
0173     rc = devm_request_irq(dev, lpc_snoop->irq,
0174                   aspeed_lpc_snoop_irq, IRQF_SHARED,
0175                   DEVICE_NAME, lpc_snoop);
0176     if (rc < 0) {
0177         dev_warn(dev, "Unable to request IRQ %d\n", lpc_snoop->irq);
0178         lpc_snoop->irq = 0;
0179         return rc;
0180     }
0181 
0182     return 0;
0183 }
0184 
0185 static int aspeed_lpc_enable_snoop(struct aspeed_lpc_snoop *lpc_snoop,
0186                    struct device *dev,
0187                    int channel, u16 lpc_port)
0188 {
0189     int rc = 0;
0190     u32 hicr5_en, snpwadr_mask, snpwadr_shift, hicrb_en;
0191     const struct aspeed_lpc_snoop_model_data *model_data =
0192         of_device_get_match_data(dev);
0193 
0194     init_waitqueue_head(&lpc_snoop->chan[channel].wq);
0195     /* Create FIFO datastructure */
0196     rc = kfifo_alloc(&lpc_snoop->chan[channel].fifo,
0197              SNOOP_FIFO_SIZE, GFP_KERNEL);
0198     if (rc)
0199         return rc;
0200 
0201     lpc_snoop->chan[channel].miscdev.minor = MISC_DYNAMIC_MINOR;
0202     lpc_snoop->chan[channel].miscdev.name =
0203         devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel);
0204     lpc_snoop->chan[channel].miscdev.fops = &snoop_fops;
0205     lpc_snoop->chan[channel].miscdev.parent = dev;
0206     rc = misc_register(&lpc_snoop->chan[channel].miscdev);
0207     if (rc)
0208         return rc;
0209 
0210     /* Enable LPC snoop channel at requested port */
0211     switch (channel) {
0212     case 0:
0213         hicr5_en = HICR5_EN_SNP0W | HICR5_ENINT_SNP0W;
0214         snpwadr_mask = SNPWADR_CH0_MASK;
0215         snpwadr_shift = SNPWADR_CH0_SHIFT;
0216         hicrb_en = HICRB_ENSNP0D;
0217         break;
0218     case 1:
0219         hicr5_en = HICR5_EN_SNP1W | HICR5_ENINT_SNP1W;
0220         snpwadr_mask = SNPWADR_CH1_MASK;
0221         snpwadr_shift = SNPWADR_CH1_SHIFT;
0222         hicrb_en = HICRB_ENSNP1D;
0223         break;
0224     default:
0225         return -EINVAL;
0226     }
0227 
0228     regmap_update_bits(lpc_snoop->regmap, HICR5, hicr5_en, hicr5_en);
0229     regmap_update_bits(lpc_snoop->regmap, SNPWADR, snpwadr_mask,
0230                lpc_port << snpwadr_shift);
0231     if (model_data->has_hicrb_ensnp)
0232         regmap_update_bits(lpc_snoop->regmap, HICRB,
0233                 hicrb_en, hicrb_en);
0234 
0235     return rc;
0236 }
0237 
0238 static void aspeed_lpc_disable_snoop(struct aspeed_lpc_snoop *lpc_snoop,
0239                      int channel)
0240 {
0241     switch (channel) {
0242     case 0:
0243         regmap_update_bits(lpc_snoop->regmap, HICR5,
0244                    HICR5_EN_SNP0W | HICR5_ENINT_SNP0W,
0245                    0);
0246         break;
0247     case 1:
0248         regmap_update_bits(lpc_snoop->regmap, HICR5,
0249                    HICR5_EN_SNP1W | HICR5_ENINT_SNP1W,
0250                    0);
0251         break;
0252     default:
0253         return;
0254     }
0255 
0256     kfifo_free(&lpc_snoop->chan[channel].fifo);
0257     misc_deregister(&lpc_snoop->chan[channel].miscdev);
0258 }
0259 
0260 static int aspeed_lpc_snoop_probe(struct platform_device *pdev)
0261 {
0262     struct aspeed_lpc_snoop *lpc_snoop;
0263     struct device *dev;
0264     struct device_node *np;
0265     u32 port;
0266     int rc;
0267 
0268     dev = &pdev->dev;
0269 
0270     lpc_snoop = devm_kzalloc(dev, sizeof(*lpc_snoop), GFP_KERNEL);
0271     if (!lpc_snoop)
0272         return -ENOMEM;
0273 
0274     np = pdev->dev.parent->of_node;
0275     if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") &&
0276         !of_device_is_compatible(np, "aspeed,ast2500-lpc-v2") &&
0277         !of_device_is_compatible(np, "aspeed,ast2600-lpc-v2")) {
0278         dev_err(dev, "unsupported LPC device binding\n");
0279         return -ENODEV;
0280     }
0281 
0282     lpc_snoop->regmap = syscon_node_to_regmap(np);
0283     if (IS_ERR(lpc_snoop->regmap)) {
0284         dev_err(dev, "Couldn't get regmap\n");
0285         return -ENODEV;
0286     }
0287 
0288     dev_set_drvdata(&pdev->dev, lpc_snoop);
0289 
0290     rc = of_property_read_u32_index(dev->of_node, "snoop-ports", 0, &port);
0291     if (rc) {
0292         dev_err(dev, "no snoop ports configured\n");
0293         return -ENODEV;
0294     }
0295 
0296     lpc_snoop->clk = devm_clk_get(dev, NULL);
0297     if (IS_ERR(lpc_snoop->clk)) {
0298         rc = PTR_ERR(lpc_snoop->clk);
0299         if (rc != -EPROBE_DEFER)
0300             dev_err(dev, "couldn't get clock\n");
0301         return rc;
0302     }
0303     rc = clk_prepare_enable(lpc_snoop->clk);
0304     if (rc) {
0305         dev_err(dev, "couldn't enable clock\n");
0306         return rc;
0307     }
0308 
0309     rc = aspeed_lpc_snoop_config_irq(lpc_snoop, pdev);
0310     if (rc)
0311         goto err;
0312 
0313     rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 0, port);
0314     if (rc)
0315         goto err;
0316 
0317     /* Configuration of 2nd snoop channel port is optional */
0318     if (of_property_read_u32_index(dev->of_node, "snoop-ports",
0319                        1, &port) == 0) {
0320         rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 1, port);
0321         if (rc) {
0322             aspeed_lpc_disable_snoop(lpc_snoop, 0);
0323             goto err;
0324         }
0325     }
0326 
0327     return 0;
0328 
0329 err:
0330     clk_disable_unprepare(lpc_snoop->clk);
0331 
0332     return rc;
0333 }
0334 
0335 static int aspeed_lpc_snoop_remove(struct platform_device *pdev)
0336 {
0337     struct aspeed_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev);
0338 
0339     /* Disable both snoop channels */
0340     aspeed_lpc_disable_snoop(lpc_snoop, 0);
0341     aspeed_lpc_disable_snoop(lpc_snoop, 1);
0342 
0343     clk_disable_unprepare(lpc_snoop->clk);
0344 
0345     return 0;
0346 }
0347 
0348 static const struct aspeed_lpc_snoop_model_data ast2400_model_data = {
0349     .has_hicrb_ensnp = 0,
0350 };
0351 
0352 static const struct aspeed_lpc_snoop_model_data ast2500_model_data = {
0353     .has_hicrb_ensnp = 1,
0354 };
0355 
0356 static const struct of_device_id aspeed_lpc_snoop_match[] = {
0357     { .compatible = "aspeed,ast2400-lpc-snoop",
0358       .data = &ast2400_model_data },
0359     { .compatible = "aspeed,ast2500-lpc-snoop",
0360       .data = &ast2500_model_data },
0361     { .compatible = "aspeed,ast2600-lpc-snoop",
0362       .data = &ast2500_model_data },
0363     { },
0364 };
0365 
0366 static struct platform_driver aspeed_lpc_snoop_driver = {
0367     .driver = {
0368         .name       = DEVICE_NAME,
0369         .of_match_table = aspeed_lpc_snoop_match,
0370     },
0371     .probe = aspeed_lpc_snoop_probe,
0372     .remove = aspeed_lpc_snoop_remove,
0373 };
0374 
0375 module_platform_driver(aspeed_lpc_snoop_driver);
0376 
0377 MODULE_DEVICE_TABLE(of, aspeed_lpc_snoop_match);
0378 MODULE_LICENSE("GPL");
0379 MODULE_AUTHOR("Robert Lippert <rlippert@google.com>");
0380 MODULE_DESCRIPTION("Linux driver to control Aspeed LPC snoop functionality");