Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 74813ceb authored by Raveendra Padasalagi's avatar Raveendra Padasalagi Committed by Dmitry Torokhov
Browse files

Input: bcm_iproc_tsc - use syscon to access shared registers



In Cygnus SOC touch screen controller registers are shared with ADC and
flex timer. Using readl/writel could lead to race condition. So touch
screen driver is enhanced to support register access using syscon framework
API's to take care of mutually exclusive access.

Signed-off-by: default avatarRaveendra Padasalagi <raveendra.padasalagi@broadcom.com>
Reviewed-by: default avatarRay Jui <ray.jui@broadcom.com>
Reviewed-by: default avatarScott Branden <scott.branden@broadcom.com>
Acked-by: default avatarRob Herring <robh@kernel.org>
Acked-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 20aa787e
Loading
Loading
Loading
Loading
+16 −5
Original line number Diff line number Diff line
@@ -2,11 +2,17 @@

Required properties:
- compatible: must be "brcm,iproc-touchscreen"
- reg: physical base address of the controller and length of memory mapped
  region.
- ts_syscon: handler of syscon node defining physical base
  address of the controller and length of memory mapped region.
  If this property is selected please make sure MFD_SYSCON config
  is enabled in the defconfig file.
- clocks:  The clock provided by the SOC to driver the tsc
- clock-name:  name for the clock
- interrupts: The touchscreen controller's interrupt
- address-cells: Specify the number of u32 entries needed in child nodes.
  Should set to 1.
- size-cells: Specify number of u32 entries needed to specify child nodes size
  in reg property. Should set to 1.

Optional properties:
- scanning_period: Time between scans. Each step is 1024 us.  Valid 1-256.
@@ -53,13 +59,18 @@ Optional properties:
- touchscreen-inverted-x: X axis is inverted (boolean)
- touchscreen-inverted-y: Y axis is inverted (boolean)

Example:
Example: An example of touchscreen node

	touchscreen: tsc@0x180A6000 {
	ts_adc_syscon: ts_adc_syscon@180a6000 {
		compatible = "brcm,iproc-ts-adc-syscon","syscon";
		reg = <0x180a6000 0xc30>;
	};

	touchscreen: touchscreen@180A6000 {
		compatible = "brcm,iproc-touchscreen";
		#address-cells = <1>;
		#size-cells = <1>;
		reg = <0x180A6000 0x40>;
		ts_syscon = <&ts_adc_syscon>;
		clocks = <&adc_clk>;
		clock-names = "tsc_clk";
		interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>;
+9 −2
Original line number Diff line number Diff line
@@ -351,9 +351,16 @@
					<&pinctrl 142 10 1>;
		};

		touchscreen: tsc@180a6000 {
		ts_adc_syscon: ts_adc_syscon@180a6000 {
			compatible = "brcm,iproc-ts-adc-syscon", "syscon";
			reg = <0x180a6000 0xc30>;
		};

		touchscreen: touchscreen@180a6000 {
			compatible = "brcm,iproc-touchscreen";
			reg = <0x180a6000 0x40>;
			#address-cells = <1>;
			#size-cells = <1>;
			ts_syscon = <&ts_adc_syscon>;
			clocks = <&asiu_clks BCM_CYGNUS_ASIU_ADC_CLK>;
			clock-names = "tsc_clk";
			interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>;
+43 −34
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/serio.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>

#define IPROC_TS_NAME "iproc-ts"

@@ -88,7 +90,11 @@
#define TS_WIRE_MODE_BIT        BIT(1)

#define dbg_reg(dev, priv, reg) \
	dev_dbg(dev, "%20s= 0x%08x\n", #reg, readl((priv)->regs + reg))
do { \
	u32 val; \
	regmap_read(priv->regmap, reg, &val); \
	dev_dbg(dev, "%20s= 0x%08x\n", #reg, val); \
} while (0)

struct tsc_param {
	/* Each step is 1024 us.  Valid 1-256 */
@@ -141,7 +147,7 @@ struct iproc_ts_priv {
	struct platform_device *pdev;
	struct input_dev *idev;

	void __iomem *regs;
	struct regmap *regmap;
	struct clk *tsc_clk;

	int  pen_status;
@@ -196,17 +202,17 @@ static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data)
	int i;
	bool needs_sync = false;

	intr_status = readl(priv->regs + INTERRUPT_STATUS);
	regmap_read(priv->regmap, INTERRUPT_STATUS, &intr_status);
	intr_status &= TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK;
	if (intr_status == 0)
		return IRQ_NONE;

	/* Clear all interrupt status bits, write-1-clear */
	writel(intr_status, priv->regs + INTERRUPT_STATUS);

	regmap_write(priv->regmap, INTERRUPT_STATUS, intr_status);
	/* Pen up/down */
	if (intr_status & TS_PEN_INTR_MASK) {
		if (readl(priv->regs + CONTROLLER_STATUS) & TS_PEN_DOWN)
		regmap_read(priv->regmap, CONTROLLER_STATUS, &priv->pen_status);
		if (priv->pen_status & TS_PEN_DOWN)
			priv->pen_status = PEN_DOWN_STATUS;
		else
			priv->pen_status = PEN_UP_STATUS;
@@ -221,7 +227,7 @@ static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data)
	/* coordinates in FIFO exceed the theshold */
	if (intr_status & TS_FIFO_INTR_MASK) {
		for (i = 0; i < priv->cfg_params.fifo_threshold; i++) {
			raw_coordinate = readl(priv->regs + FIFO_DATA);
			regmap_read(priv->regmap, FIFO_DATA, &raw_coordinate);
			if (raw_coordinate == INVALID_COORD)
				continue;

@@ -239,7 +245,7 @@ static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data)
			x = (x >> 4) & 0x0FFF;
			y = (y >> 4) & 0x0FFF;

			/* adjust x y according to lcd tsc mount angle */
			/* Adjust x y according to LCD tsc mount angle. */
			if (priv->cfg_params.invert_x)
				x = priv->cfg_params.max_x - x;

@@ -262,9 +268,10 @@ static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data)

static int iproc_ts_start(struct input_dev *idev)
{
	struct iproc_ts_priv *priv = input_get_drvdata(idev);
	u32 val;
	u32 mask;
	int error;
	struct iproc_ts_priv *priv = input_get_drvdata(idev);

	/* Enable clock */
	error = clk_prepare_enable(priv->tsc_clk);
@@ -279,9 +286,10 @@ static int iproc_ts_start(struct input_dev *idev)
	 *  FIFO reaches the int_th value, and pen event(up/down)
	 */
	val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK;
	writel(val, priv->regs + INTERRUPT_MASK);
	regmap_update_bits(priv->regmap, INTERRUPT_MASK, val, val);

	writel(priv->cfg_params.fifo_threshold, priv->regs + INTERRUPT_THRES);
	val = priv->cfg_params.fifo_threshold;
	regmap_write(priv->regmap, INTERRUPT_THRES, val);

	/* Initialize control reg1 */
	val = 0;
@@ -289,26 +297,23 @@ static int iproc_ts_start(struct input_dev *idev)
	val |= priv->cfg_params.debounce_timeout << DEBOUNCE_TIMEOUT_SHIFT;
	val |= priv->cfg_params.settling_timeout << SETTLING_TIMEOUT_SHIFT;
	val |= priv->cfg_params.touch_timeout << TOUCH_TIMEOUT_SHIFT;
	writel(val, priv->regs + REGCTL1);
	regmap_write(priv->regmap, REGCTL1, val);

	/* Try to clear all interrupt status */
	val = readl(priv->regs + INTERRUPT_STATUS);
	val |= TS_FIFO_INTR_MASK | TS_PEN_INTR_MASK;
	writel(val, priv->regs + INTERRUPT_STATUS);
	val = TS_FIFO_INTR_MASK | TS_PEN_INTR_MASK;
	regmap_update_bits(priv->regmap, INTERRUPT_STATUS, val, val);

	/* Initialize control reg2 */
	val = readl(priv->regs + REGCTL2);
	val |= TS_CONTROLLER_EN_BIT | TS_WIRE_MODE_BIT;

	val &= ~TS_CONTROLLER_AVGDATA_MASK;
	val = TS_CONTROLLER_EN_BIT | TS_WIRE_MODE_BIT;
	val |= priv->cfg_params.average_data << TS_CONTROLLER_AVGDATA_SHIFT;

	val &= ~(TS_CONTROLLER_PWR_LDO |	/* PWR up LDO */
	mask = (TS_CONTROLLER_AVGDATA_MASK);
	mask |= (TS_CONTROLLER_PWR_LDO |	/* PWR up LDO */
		   TS_CONTROLLER_PWR_ADC |	/* PWR up ADC */
		   TS_CONTROLLER_PWR_BGP |	/* PWR up BGP */
		   TS_CONTROLLER_PWR_TS);	/* PWR up TS */

	writel(val, priv->regs + REGCTL2);
	mask |= val;
	regmap_update_bits(priv->regmap, REGCTL2, mask, val);

	ts_reg_dump(priv);

@@ -320,12 +325,17 @@ static void iproc_ts_stop(struct input_dev *dev)
	u32 val;
	struct iproc_ts_priv *priv = input_get_drvdata(dev);

	writel(0, priv->regs + INTERRUPT_MASK); /* Disable all interrupts */
	/*
	 * Disable FIFO int_th and pen event(up/down)Interrupts only
	 * as the interrupt mask register is shared between ADC, TS and
	 * flextimer.
	 */
	val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK;
	regmap_update_bits(priv->regmap, INTERRUPT_MASK, val, 0);

	/* Only power down touch screen controller */
	val = readl(priv->regs + REGCTL2);
	val |= TS_CONTROLLER_PWR_TS;
	writel(val, priv->regs + REGCTL2);
	val = TS_CONTROLLER_PWR_TS;
	regmap_update_bits(priv->regmap, REGCTL2, val, val);

	clk_disable(priv->tsc_clk);
}
@@ -414,7 +424,6 @@ static int iproc_ts_probe(struct platform_device *pdev)
{
	struct iproc_ts_priv *priv;
	struct input_dev *idev;
	struct resource *res;
	int irq;
	int error;

@@ -422,11 +431,11 @@ static int iproc_ts_probe(struct platform_device *pdev)
	if (!priv)
		return -ENOMEM;

	/* touchscreen controller memory mapped regs */
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	priv->regs = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(priv->regs)) {
		error = PTR_ERR(priv->regs);
	/* touchscreen controller memory mapped regs via syscon*/
	priv->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
							"ts_syscon");
	if (IS_ERR(priv->regmap)) {
		error = PTR_ERR(priv->regmap);
		dev_err(&pdev->dev, "unable to map I/O memory:%d\n", error);
		return error;
	}