Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
0004  */
0005 
0006 #include <linux/clk.h>
0007 #include <linux/interrupt.h>
0008 #include <linux/irqchip/chained_irq.h>
0009 #include <linux/irqdesc.h>
0010 #include <linux/irqdomain.h>
0011 #include <linux/irq.h>
0012 #include <linux/mfd/imx25-tsadc.h>
0013 #include <linux/module.h>
0014 #include <linux/of.h>
0015 #include <linux/of_platform.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/regmap.h>
0018 
0019 static struct regmap_config mx25_tsadc_regmap_config = {
0020     .fast_io = true,
0021     .max_register = 8,
0022     .reg_bits = 32,
0023     .val_bits = 32,
0024     .reg_stride = 4,
0025 };
0026 
0027 static void mx25_tsadc_irq_handler(struct irq_desc *desc)
0028 {
0029     struct mx25_tsadc *tsadc = irq_desc_get_handler_data(desc);
0030     struct irq_chip *chip = irq_desc_get_chip(desc);
0031     u32 status;
0032 
0033     chained_irq_enter(chip, desc);
0034 
0035     regmap_read(tsadc->regs, MX25_TSC_TGSR, &status);
0036 
0037     if (status & MX25_TGSR_GCQ_INT)
0038         generic_handle_domain_irq(tsadc->domain, 1);
0039 
0040     if (status & MX25_TGSR_TCQ_INT)
0041         generic_handle_domain_irq(tsadc->domain, 0);
0042 
0043     chained_irq_exit(chip, desc);
0044 }
0045 
0046 static int mx25_tsadc_domain_map(struct irq_domain *d, unsigned int irq,
0047                  irq_hw_number_t hwirq)
0048 {
0049     struct mx25_tsadc *tsadc = d->host_data;
0050 
0051     irq_set_chip_data(irq, tsadc);
0052     irq_set_chip_and_handler(irq, &dummy_irq_chip,
0053                  handle_level_irq);
0054     irq_modify_status(irq, IRQ_NOREQUEST, IRQ_NOPROBE);
0055 
0056     return 0;
0057 }
0058 
0059 static const struct irq_domain_ops mx25_tsadc_domain_ops = {
0060     .map = mx25_tsadc_domain_map,
0061     .xlate = irq_domain_xlate_onecell,
0062 };
0063 
0064 static int mx25_tsadc_setup_irq(struct platform_device *pdev,
0065                 struct mx25_tsadc *tsadc)
0066 {
0067     struct device *dev = &pdev->dev;
0068     struct device_node *np = dev->of_node;
0069     int irq;
0070 
0071     irq = platform_get_irq(pdev, 0);
0072     if (irq <= 0)
0073         return irq;
0074 
0075     tsadc->domain = irq_domain_add_simple(np, 2, 0, &mx25_tsadc_domain_ops,
0076                           tsadc);
0077     if (!tsadc->domain) {
0078         dev_err(dev, "Failed to add irq domain\n");
0079         return -ENOMEM;
0080     }
0081 
0082     irq_set_chained_handler_and_data(irq, mx25_tsadc_irq_handler, tsadc);
0083 
0084     return 0;
0085 }
0086 
0087 static void mx25_tsadc_setup_clk(struct platform_device *pdev,
0088                  struct mx25_tsadc *tsadc)
0089 {
0090     unsigned clk_div;
0091 
0092     /*
0093      * According to the datasheet the ADC clock should never
0094      * exceed 1,75 MHz. Base clock is the IPG and the ADC unit uses
0095      * a funny clock divider. To keep the ADC conversion time constant
0096      * adapt the ADC internal clock divider to the IPG clock rate.
0097      */
0098 
0099     dev_dbg(&pdev->dev, "Found master clock at %lu Hz\n",
0100         clk_get_rate(tsadc->clk));
0101 
0102     clk_div = DIV_ROUND_UP(clk_get_rate(tsadc->clk), 1750000);
0103     dev_dbg(&pdev->dev, "Setting up ADC clock divider to %u\n", clk_div);
0104 
0105     /* adc clock = IPG clock / (2 * div + 2) */
0106     clk_div -= 2;
0107     clk_div /= 2;
0108 
0109     /*
0110      * the ADC clock divider changes its behaviour when values below 4
0111      * are used: it is fixed to "/ 10" in this case
0112      */
0113     clk_div = max_t(unsigned, 4, clk_div);
0114 
0115     dev_dbg(&pdev->dev, "Resulting ADC conversion clock at %lu Hz\n",
0116         clk_get_rate(tsadc->clk) / (2 * clk_div + 2));
0117 
0118     regmap_update_bits(tsadc->regs, MX25_TSC_TGCR,
0119                MX25_TGCR_ADCCLKCFG(0x1f),
0120                MX25_TGCR_ADCCLKCFG(clk_div));
0121 }
0122 
0123 static int mx25_tsadc_probe(struct platform_device *pdev)
0124 {
0125     struct device *dev = &pdev->dev;
0126     struct mx25_tsadc *tsadc;
0127     struct resource *res;
0128     int ret;
0129     void __iomem *iomem;
0130 
0131     tsadc = devm_kzalloc(dev, sizeof(*tsadc), GFP_KERNEL);
0132     if (!tsadc)
0133         return -ENOMEM;
0134 
0135     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0136     iomem = devm_ioremap_resource(dev, res);
0137     if (IS_ERR(iomem))
0138         return PTR_ERR(iomem);
0139 
0140     tsadc->regs = devm_regmap_init_mmio(dev, iomem,
0141                         &mx25_tsadc_regmap_config);
0142     if (IS_ERR(tsadc->regs)) {
0143         dev_err(dev, "Failed to initialize regmap\n");
0144         return PTR_ERR(tsadc->regs);
0145     }
0146 
0147     tsadc->clk = devm_clk_get(dev, "ipg");
0148     if (IS_ERR(tsadc->clk)) {
0149         dev_err(dev, "Failed to get ipg clock\n");
0150         return PTR_ERR(tsadc->clk);
0151     }
0152 
0153     /* setup clock according to the datasheet */
0154     mx25_tsadc_setup_clk(pdev, tsadc);
0155 
0156     /* Enable clock and reset the component */
0157     regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_CLK_EN,
0158                MX25_TGCR_CLK_EN);
0159     regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_TSC_RST,
0160                MX25_TGCR_TSC_RST);
0161 
0162     /* Setup powersaving mode, but enable internal reference voltage */
0163     regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_POWERMODE_MASK,
0164                MX25_TGCR_POWERMODE_SAVE);
0165     regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_INTREFEN,
0166                MX25_TGCR_INTREFEN);
0167 
0168     ret = mx25_tsadc_setup_irq(pdev, tsadc);
0169     if (ret)
0170         return ret;
0171 
0172     platform_set_drvdata(pdev, tsadc);
0173 
0174     return devm_of_platform_populate(dev);
0175 }
0176 
0177 static int mx25_tsadc_remove(struct platform_device *pdev)
0178 {
0179     struct mx25_tsadc *tsadc = platform_get_drvdata(pdev);
0180     int irq = platform_get_irq(pdev, 0);
0181 
0182     if (irq) {
0183         irq_set_chained_handler_and_data(irq, NULL, NULL);
0184         irq_domain_remove(tsadc->domain);
0185     }
0186 
0187     return 0;
0188 }
0189 
0190 static const struct of_device_id mx25_tsadc_ids[] = {
0191     { .compatible = "fsl,imx25-tsadc" },
0192     { /* Sentinel */ }
0193 };
0194 MODULE_DEVICE_TABLE(of, mx25_tsadc_ids);
0195 
0196 static struct platform_driver mx25_tsadc_driver = {
0197     .driver = {
0198         .name = "mx25-tsadc",
0199         .of_match_table = mx25_tsadc_ids,
0200     },
0201     .probe = mx25_tsadc_probe,
0202     .remove = mx25_tsadc_remove,
0203 };
0204 module_platform_driver(mx25_tsadc_driver);
0205 
0206 MODULE_DESCRIPTION("MFD for ADC/TSC for Freescale mx25");
0207 MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
0208 MODULE_LICENSE("GPL v2");
0209 MODULE_ALIAS("platform:mx25-tsadc");