brintos

brintos / linux-shallow public Read only

0
0
Text · 10.1 KiB · 0c1364d Raw
389 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * TI Touch Screen / ADC MFD driver4 *5 * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/6 */7 8#include <linux/module.h>9#include <linux/slab.h>10#include <linux/err.h>11#include <linux/io.h>12#include <linux/clk.h>13#include <linux/regmap.h>14#include <linux/mfd/core.h>15#include <linux/pm_runtime.h>16#include <linux/of.h>17#include <linux/platform_device.h>18#include <linux/sched.h>19 20#include <linux/mfd/ti_am335x_tscadc.h>21 22static const struct regmap_config tscadc_regmap_config = {23	.name = "ti_tscadc",24	.reg_bits = 32,25	.reg_stride = 4,26	.val_bits = 32,27};28 29void am335x_tsc_se_set_cache(struct ti_tscadc_dev *tscadc, u32 val)30{31	unsigned long flags;32 33	spin_lock_irqsave(&tscadc->reg_lock, flags);34	tscadc->reg_se_cache |= val;35	if (tscadc->adc_waiting)36		wake_up(&tscadc->reg_se_wait);37	else if (!tscadc->adc_in_use)38		regmap_write(tscadc->regmap, REG_SE, tscadc->reg_se_cache);39 40	spin_unlock_irqrestore(&tscadc->reg_lock, flags);41}42EXPORT_SYMBOL_GPL(am335x_tsc_se_set_cache);43 44static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tscadc)45{46	DEFINE_WAIT(wait);47	u32 reg;48 49	regmap_read(tscadc->regmap, REG_ADCFSM, &reg);50	if (reg & SEQ_STATUS) {51		tscadc->adc_waiting = true;52		prepare_to_wait(&tscadc->reg_se_wait, &wait,53				TASK_UNINTERRUPTIBLE);54		spin_unlock_irq(&tscadc->reg_lock);55 56		schedule();57 58		spin_lock_irq(&tscadc->reg_lock);59		finish_wait(&tscadc->reg_se_wait, &wait);60 61		/*62		 * Sequencer should either be idle or63		 * busy applying the charge step.64		 */65		regmap_read(tscadc->regmap, REG_ADCFSM, &reg);66		WARN_ON((reg & SEQ_STATUS) && !(reg & CHARGE_STEP));67		tscadc->adc_waiting = false;68	}69	tscadc->adc_in_use = true;70}71 72void am335x_tsc_se_set_once(struct ti_tscadc_dev *tscadc, u32 val)73{74	spin_lock_irq(&tscadc->reg_lock);75	am335x_tscadc_need_adc(tscadc);76 77	regmap_write(tscadc->regmap, REG_SE, val);78	spin_unlock_irq(&tscadc->reg_lock);79}80EXPORT_SYMBOL_GPL(am335x_tsc_se_set_once);81 82void am335x_tsc_se_adc_done(struct ti_tscadc_dev *tscadc)83{84	unsigned long flags;85 86	spin_lock_irqsave(&tscadc->reg_lock, flags);87	tscadc->adc_in_use = false;88	regmap_write(tscadc->regmap, REG_SE, tscadc->reg_se_cache);89	spin_unlock_irqrestore(&tscadc->reg_lock, flags);90}91EXPORT_SYMBOL_GPL(am335x_tsc_se_adc_done);92 93void am335x_tsc_se_clr(struct ti_tscadc_dev *tscadc, u32 val)94{95	unsigned long flags;96 97	spin_lock_irqsave(&tscadc->reg_lock, flags);98	tscadc->reg_se_cache &= ~val;99	regmap_write(tscadc->regmap, REG_SE, tscadc->reg_se_cache);100	spin_unlock_irqrestore(&tscadc->reg_lock, flags);101}102EXPORT_SYMBOL_GPL(am335x_tsc_se_clr);103 104static void tscadc_idle_config(struct ti_tscadc_dev *tscadc)105{106	unsigned int idleconfig;107 108	idleconfig = STEPCONFIG_INM_ADCREFM | STEPCONFIG_INP_ADCREFM;109	if (ti_adc_with_touchscreen(tscadc))110		idleconfig |= STEPCONFIG_YNN | STEPCONFIG_YPN;111 112	regmap_write(tscadc->regmap, REG_IDLECONFIG, idleconfig);113}114 115static	int ti_tscadc_probe(struct platform_device *pdev)116{117	struct ti_tscadc_dev *tscadc;118	struct resource *res;119	struct clk *clk;120	struct device_node *node;121	struct mfd_cell *cell;122	bool use_tsc = false, use_mag = false;123	u32 val;124	int err;125	int tscmag_wires = 0, adc_channels = 0, cell_idx = 0, total_channels;126	int readouts = 0, mag_tracks = 0;127 128	/* Allocate memory for device */129	tscadc = devm_kzalloc(&pdev->dev, sizeof(*tscadc), GFP_KERNEL);130	if (!tscadc)131		return -ENOMEM;132 133	tscadc->dev = &pdev->dev;134 135	if (!pdev->dev.of_node) {136		dev_err(&pdev->dev, "Could not find valid DT data.\n");137		return -EINVAL;138	}139 140	tscadc->data = of_device_get_match_data(&pdev->dev);141 142	if (ti_adc_with_touchscreen(tscadc)) {143		node = of_get_child_by_name(pdev->dev.of_node, "tsc");144		of_property_read_u32(node, "ti,wires", &tscmag_wires);145		err = of_property_read_u32(node, "ti,coordinate-readouts",146					   &readouts);147		if (err < 0)148			of_property_read_u32(node, "ti,coordiante-readouts",149					     &readouts);150 151		of_node_put(node);152 153		if (tscmag_wires)154			use_tsc = true;155	} else {156		/*157		 * When adding support for the magnetic stripe reader, here is158		 * the place to look for the number of tracks used from device159		 * tree. Let's default to 0 for now.160		 */161		mag_tracks = 0;162		tscmag_wires = mag_tracks * 2;163		if (tscmag_wires)164			use_mag = true;165	}166 167	node = of_get_child_by_name(pdev->dev.of_node, "adc");168	of_property_for_each_u32(node, "ti,adc-channels", val) {169		adc_channels++;170		if (val > 7) {171			dev_err(&pdev->dev, " PIN numbers are 0..7 (not %d)\n",172				val);173			of_node_put(node);174			return -EINVAL;175		}176	}177 178	of_node_put(node);179 180	total_channels = tscmag_wires + adc_channels;181	if (total_channels > 8) {182		dev_err(&pdev->dev, "Number of i/p channels more than 8\n");183		return -EINVAL;184	}185 186	if (total_channels == 0) {187		dev_err(&pdev->dev, "Need at least one channel.\n");188		return -EINVAL;189	}190 191	if (use_tsc && (readouts * 2 + 2 + adc_channels > 16)) {192		dev_err(&pdev->dev, "Too many step configurations requested\n");193		return -EINVAL;194	}195 196	err = platform_get_irq(pdev, 0);197	if (err < 0)198		return err;199	else200		tscadc->irq = err;201 202	tscadc->tscadc_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);203	if (IS_ERR(tscadc->tscadc_base))204		return PTR_ERR(tscadc->tscadc_base);205 206	tscadc->tscadc_phys_base = res->start;207	tscadc->regmap = devm_regmap_init_mmio(&pdev->dev,208					       tscadc->tscadc_base,209					       &tscadc_regmap_config);210	if (IS_ERR(tscadc->regmap)) {211		dev_err(&pdev->dev, "regmap init failed\n");212		return PTR_ERR(tscadc->regmap);213	}214 215	spin_lock_init(&tscadc->reg_lock);216	init_waitqueue_head(&tscadc->reg_se_wait);217 218	pm_runtime_enable(&pdev->dev);219	pm_runtime_get_sync(&pdev->dev);220 221	/*222	 * The TSC_ADC_Subsystem has 2 clock domains: OCP_CLK and ADC_CLK.223	 * ADCs produce a 12-bit sample every 15 ADC_CLK cycles.224	 * am33xx ADCs expect to capture 200ksps.225	 * am47xx ADCs expect to capture 867ksps.226	 * We need ADC clocks respectively running at 3MHz and 13MHz.227	 * These frequencies are valid since TSC_ADC_SS controller design228	 * assumes the OCP clock is at least 6x faster than the ADC clock.229	 */230	clk = devm_clk_get(&pdev->dev, NULL);231	if (IS_ERR(clk)) {232		dev_err(&pdev->dev, "failed to get fck\n");233		err = PTR_ERR(clk);234		goto err_disable_clk;235	}236 237	tscadc->clk_div = (clk_get_rate(clk) / tscadc->data->target_clk_rate) - 1;238	regmap_write(tscadc->regmap, REG_CLKDIV, tscadc->clk_div);239 240	/*241	 * Set the control register bits. tscadc->ctrl stores the configuration242	 * of the CTRL register but not the subsystem enable bit which must be243	 * added manually when timely.244	 */245	tscadc->ctrl = CNTRLREG_STEPID;246	if (ti_adc_with_touchscreen(tscadc)) {247		tscadc->ctrl |= CNTRLREG_TSC_STEPCONFIGWRT;248		if (use_tsc) {249			tscadc->ctrl |= CNTRLREG_TSC_ENB;250			if (tscmag_wires == 5)251				tscadc->ctrl |= CNTRLREG_TSC_5WIRE;252			else253				tscadc->ctrl |= CNTRLREG_TSC_4WIRE;254		}255	} else {256		tscadc->ctrl |= CNTRLREG_MAG_PREAMP_PWRDOWN |257				CNTRLREG_MAG_PREAMP_BYPASS;258	}259	regmap_write(tscadc->regmap, REG_CTRL, tscadc->ctrl);260 261	tscadc_idle_config(tscadc);262 263	/* Enable the TSC module enable bit */264	regmap_write(tscadc->regmap, REG_CTRL, tscadc->ctrl | CNTRLREG_SSENB);265 266	/* TSC or MAG Cell */267	if (use_tsc || use_mag) {268		cell = &tscadc->cells[cell_idx++];269		cell->name = tscadc->data->secondary_feature_name;270		cell->of_compatible = tscadc->data->secondary_feature_compatible;271		cell->platform_data = &tscadc;272		cell->pdata_size = sizeof(tscadc);273	}274 275	/* ADC Cell */276	if (adc_channels > 0) {277		cell = &tscadc->cells[cell_idx++];278		cell->name = tscadc->data->adc_feature_name;279		cell->of_compatible = tscadc->data->adc_feature_compatible;280		cell->platform_data = &tscadc;281		cell->pdata_size = sizeof(tscadc);282	}283 284	err = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,285			      tscadc->cells, cell_idx, NULL, 0, NULL);286	if (err < 0)287		goto err_disable_clk;288 289	platform_set_drvdata(pdev, tscadc);290	return 0;291 292err_disable_clk:293	pm_runtime_put_sync(&pdev->dev);294	pm_runtime_disable(&pdev->dev);295 296	return err;297}298 299static void ti_tscadc_remove(struct platform_device *pdev)300{301	struct ti_tscadc_dev *tscadc = platform_get_drvdata(pdev);302 303	regmap_write(tscadc->regmap, REG_SE, 0x00);304 305	pm_runtime_put_sync(&pdev->dev);306	pm_runtime_disable(&pdev->dev);307 308	mfd_remove_devices(tscadc->dev);309}310 311static int __maybe_unused ti_tscadc_can_wakeup(struct device *dev, void *data)312{313	return device_may_wakeup(dev);314}315 316static int __maybe_unused tscadc_suspend(struct device *dev)317{318	struct ti_tscadc_dev *tscadc = dev_get_drvdata(dev);319 320	regmap_write(tscadc->regmap, REG_SE, 0x00);321	if (device_for_each_child(dev, NULL, ti_tscadc_can_wakeup)) {322		u32 ctrl;323 324		regmap_read(tscadc->regmap, REG_CTRL, &ctrl);325		ctrl &= ~(CNTRLREG_POWERDOWN);326		ctrl |= CNTRLREG_SSENB;327		regmap_write(tscadc->regmap, REG_CTRL, ctrl);328	}329	pm_runtime_put_sync(dev);330 331	return 0;332}333 334static int __maybe_unused tscadc_resume(struct device *dev)335{336	struct ti_tscadc_dev *tscadc = dev_get_drvdata(dev);337 338	pm_runtime_get_sync(dev);339 340	regmap_write(tscadc->regmap, REG_CLKDIV, tscadc->clk_div);341	regmap_write(tscadc->regmap, REG_CTRL, tscadc->ctrl);342	tscadc_idle_config(tscadc);343	regmap_write(tscadc->regmap, REG_CTRL, tscadc->ctrl | CNTRLREG_SSENB);344 345	return 0;346}347 348static SIMPLE_DEV_PM_OPS(tscadc_pm_ops, tscadc_suspend, tscadc_resume);349 350static const struct ti_tscadc_data tscdata = {351	.adc_feature_name = "TI-am335x-adc",352	.adc_feature_compatible = "ti,am3359-adc",353	.secondary_feature_name = "TI-am335x-tsc",354	.secondary_feature_compatible = "ti,am3359-tsc",355	.target_clk_rate = TSC_ADC_CLK,356};357 358static const struct ti_tscadc_data magdata = {359	.adc_feature_name = "TI-am43xx-adc",360	.adc_feature_compatible = "ti,am4372-adc",361	.secondary_feature_name = "TI-am43xx-mag",362	.secondary_feature_compatible = "ti,am4372-mag",363	.target_clk_rate = MAG_ADC_CLK,364};365 366static const struct of_device_id ti_tscadc_dt_ids[] = {367	{ .compatible = "ti,am3359-tscadc", .data = &tscdata },368	{ .compatible = "ti,am4372-magadc", .data = &magdata },369	{ }370};371MODULE_DEVICE_TABLE(of, ti_tscadc_dt_ids);372 373static struct platform_driver ti_tscadc_driver = {374	.driver = {375		.name   = "ti_am3359-tscadc",376		.pm	= &tscadc_pm_ops,377		.of_match_table = ti_tscadc_dt_ids,378	},379	.probe	= ti_tscadc_probe,380	.remove_new = ti_tscadc_remove,381 382};383 384module_platform_driver(ti_tscadc_driver);385 386MODULE_DESCRIPTION("TI touchscreen/magnetic stripe reader/ADC MFD controller driver");387MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");388MODULE_LICENSE("GPL");389