brintos

brintos / linux-shallow public Read only

0
0
Text · 5.5 KiB · 2e4ab24 Raw
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