222 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>4 */5 6#include <linux/clk.h>7#include <linux/interrupt.h>8#include <linux/irqchip/chained_irq.h>9#include <linux/irqdesc.h>10#include <linux/irqdomain.h>11#include <linux/irq.h>12#include <linux/mfd/imx25-tsadc.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/of_platform.h>16#include <linux/platform_device.h>17#include <linux/regmap.h>18 19static const struct regmap_config mx25_tsadc_regmap_config = {20 .fast_io = true,21 .max_register = 8,22 .reg_bits = 32,23 .val_bits = 32,24 .reg_stride = 4,25};26 27static void mx25_tsadc_irq_handler(struct irq_desc *desc)28{29 struct mx25_tsadc *tsadc = irq_desc_get_handler_data(desc);30 struct irq_chip *chip = irq_desc_get_chip(desc);31 u32 status;32 33 chained_irq_enter(chip, desc);34 35 regmap_read(tsadc->regs, MX25_TSC_TGSR, &status);36 37 if (status & MX25_TGSR_GCQ_INT)38 generic_handle_domain_irq(tsadc->domain, 1);39 40 if (status & MX25_TGSR_TCQ_INT)41 generic_handle_domain_irq(tsadc->domain, 0);42 43 chained_irq_exit(chip, desc);44}45 46static int mx25_tsadc_domain_map(struct irq_domain *d, unsigned int irq,47 irq_hw_number_t hwirq)48{49 struct mx25_tsadc *tsadc = d->host_data;50 51 irq_set_chip_data(irq, tsadc);52 irq_set_chip_and_handler(irq, &dummy_irq_chip,53 handle_level_irq);54 irq_modify_status(irq, IRQ_NOREQUEST, IRQ_NOPROBE);55 56 return 0;57}58 59static const struct irq_domain_ops mx25_tsadc_domain_ops = {60 .map = mx25_tsadc_domain_map,61 .xlate = irq_domain_xlate_onecell,62};63 64static int mx25_tsadc_setup_irq(struct platform_device *pdev,65 struct mx25_tsadc *tsadc)66{67 struct device *dev = &pdev->dev;68 struct device_node *np = dev->of_node;69 int irq;70 71 irq = platform_get_irq(pdev, 0);72 if (irq < 0)73 return irq;74 75 tsadc->domain = irq_domain_add_simple(np, 2, 0, &mx25_tsadc_domain_ops,76 tsadc);77 if (!tsadc->domain) {78 dev_err(dev, "Failed to add irq domain\n");79 return -ENOMEM;80 }81 82 irq_set_chained_handler_and_data(irq, mx25_tsadc_irq_handler, tsadc);83 84 return 0;85}86 87static int mx25_tsadc_unset_irq(struct platform_device *pdev)88{89 struct mx25_tsadc *tsadc = platform_get_drvdata(pdev);90 int irq = platform_get_irq(pdev, 0);91 92 if (irq >= 0) {93 irq_set_chained_handler_and_data(irq, NULL, NULL);94 irq_domain_remove(tsadc->domain);95 }96 97 return 0;98}99 100static void mx25_tsadc_setup_clk(struct platform_device *pdev,101 struct mx25_tsadc *tsadc)102{103 unsigned clk_div;104 105 /*106 * According to the datasheet the ADC clock should never107 * exceed 1,75 MHz. Base clock is the IPG and the ADC unit uses108 * a funny clock divider. To keep the ADC conversion time constant109 * adapt the ADC internal clock divider to the IPG clock rate.110 */111 112 dev_dbg(&pdev->dev, "Found master clock at %lu Hz\n",113 clk_get_rate(tsadc->clk));114 115 clk_div = DIV_ROUND_UP(clk_get_rate(tsadc->clk), 1750000);116 dev_dbg(&pdev->dev, "Setting up ADC clock divider to %u\n", clk_div);117 118 /* adc clock = IPG clock / (2 * div + 2) */119 clk_div -= 2;120 clk_div /= 2;121 122 /*123 * the ADC clock divider changes its behaviour when values below 4124 * are used: it is fixed to "/ 10" in this case125 */126 clk_div = max_t(unsigned, 4, clk_div);127 128 dev_dbg(&pdev->dev, "Resulting ADC conversion clock at %lu Hz\n",129 clk_get_rate(tsadc->clk) / (2 * clk_div + 2));130 131 regmap_update_bits(tsadc->regs, MX25_TSC_TGCR,132 MX25_TGCR_ADCCLKCFG(0x1f),133 MX25_TGCR_ADCCLKCFG(clk_div));134}135 136static int mx25_tsadc_probe(struct platform_device *pdev)137{138 struct device *dev = &pdev->dev;139 struct mx25_tsadc *tsadc;140 int ret;141 void __iomem *iomem;142 143 tsadc = devm_kzalloc(dev, sizeof(*tsadc), GFP_KERNEL);144 if (!tsadc)145 return -ENOMEM;146 147 iomem = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);148 if (IS_ERR(iomem))149 return PTR_ERR(iomem);150 151 tsadc->regs = devm_regmap_init_mmio(dev, iomem,152 &mx25_tsadc_regmap_config);153 if (IS_ERR(tsadc->regs)) {154 dev_err(dev, "Failed to initialize regmap\n");155 return PTR_ERR(tsadc->regs);156 }157 158 tsadc->clk = devm_clk_get(dev, "ipg");159 if (IS_ERR(tsadc->clk)) {160 dev_err(dev, "Failed to get ipg clock\n");161 return PTR_ERR(tsadc->clk);162 }163 164 /* setup clock according to the datasheet */165 mx25_tsadc_setup_clk(pdev, tsadc);166 167 /* Enable clock and reset the component */168 regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_CLK_EN,169 MX25_TGCR_CLK_EN);170 regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_TSC_RST,171 MX25_TGCR_TSC_RST);172 173 /* Setup powersaving mode, but enable internal reference voltage */174 regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_POWERMODE_MASK,175 MX25_TGCR_POWERMODE_SAVE);176 regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_INTREFEN,177 MX25_TGCR_INTREFEN);178 179 ret = mx25_tsadc_setup_irq(pdev, tsadc);180 if (ret)181 return ret;182 183 platform_set_drvdata(pdev, tsadc);184 185 ret = devm_of_platform_populate(dev);186 if (ret)187 goto err_irq;188 189 return 0;190 191err_irq:192 mx25_tsadc_unset_irq(pdev);193 194 return ret;195}196 197static void mx25_tsadc_remove(struct platform_device *pdev)198{199 mx25_tsadc_unset_irq(pdev);200}201 202static const struct of_device_id mx25_tsadc_ids[] = {203 { .compatible = "fsl,imx25-tsadc" },204 { /* Sentinel */ }205};206MODULE_DEVICE_TABLE(of, mx25_tsadc_ids);207 208static struct platform_driver mx25_tsadc_driver = {209 .driver = {210 .name = "mx25-tsadc",211 .of_match_table = mx25_tsadc_ids,212 },213 .probe = mx25_tsadc_probe,214 .remove_new = mx25_tsadc_remove,215};216module_platform_driver(mx25_tsadc_driver);217 218MODULE_DESCRIPTION("MFD for ADC/TSC for Freescale mx25");219MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");220MODULE_LICENSE("GPL v2");221MODULE_ALIAS("platform:mx25-tsadc");222