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

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
...@@ -2,11 +2,17 @@ ...@@ -2,11 +2,17 @@
Required properties: Required properties:
- compatible: must be "brcm,iproc-touchscreen" - compatible: must be "brcm,iproc-touchscreen"
- reg: physical base address of the controller and length of memory mapped - ts_syscon: handler of syscon node defining physical base
region. 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 - clocks: The clock provided by the SOC to driver the tsc
- clock-name: name for the clock - clock-name: name for the clock
- interrupts: The touchscreen controller's interrupt - 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: Optional properties:
- scanning_period: Time between scans. Each step is 1024 us. Valid 1-256. - scanning_period: Time between scans. Each step is 1024 us. Valid 1-256.
...@@ -53,13 +59,18 @@ Optional properties: ...@@ -53,13 +59,18 @@ Optional properties:
- touchscreen-inverted-x: X axis is inverted (boolean) - touchscreen-inverted-x: X axis is inverted (boolean)
- touchscreen-inverted-y: Y 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"; compatible = "brcm,iproc-touchscreen";
#address-cells = <1>; #address-cells = <1>;
#size-cells = <1>; #size-cells = <1>;
reg = <0x180A6000 0x40>; ts_syscon = <&ts_adc_syscon>;
clocks = <&adc_clk>; clocks = <&adc_clk>;
clock-names = "tsc_clk"; clock-names = "tsc_clk";
interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>; interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>;
......
...@@ -351,9 +351,16 @@ gpio_asiu: gpio@180a5000 { ...@@ -351,9 +351,16 @@ gpio_asiu: gpio@180a5000 {
<&pinctrl 142 10 1>; <&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"; 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>; clocks = <&asiu_clks BCM_CYGNUS_ASIU_ADC_CLK>;
clock-names = "tsc_clk"; clock-names = "tsc_clk";
interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>; interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>;
......
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/serio.h> #include <linux/serio.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#define IPROC_TS_NAME "iproc-ts" #define IPROC_TS_NAME "iproc-ts"
...@@ -88,7 +90,11 @@ ...@@ -88,7 +90,11 @@
#define TS_WIRE_MODE_BIT BIT(1) #define TS_WIRE_MODE_BIT BIT(1)
#define dbg_reg(dev, priv, reg) \ #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 { struct tsc_param {
/* Each step is 1024 us. Valid 1-256 */ /* Each step is 1024 us. Valid 1-256 */
...@@ -141,7 +147,7 @@ struct iproc_ts_priv { ...@@ -141,7 +147,7 @@ struct iproc_ts_priv {
struct platform_device *pdev; struct platform_device *pdev;
struct input_dev *idev; struct input_dev *idev;
void __iomem *regs; struct regmap *regmap;
struct clk *tsc_clk; struct clk *tsc_clk;
int pen_status; int pen_status;
...@@ -196,22 +202,22 @@ static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data) ...@@ -196,22 +202,22 @@ static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data)
int i; int i;
bool needs_sync = false; 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; intr_status &= TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK;
if (intr_status == 0) if (intr_status == 0)
return IRQ_NONE; return IRQ_NONE;
/* Clear all interrupt status bits, write-1-clear */ /* 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 */ /* Pen up/down */
if (intr_status & TS_PEN_INTR_MASK) { 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; priv->pen_status = PEN_DOWN_STATUS;
else else
priv->pen_status = PEN_UP_STATUS; priv->pen_status = PEN_UP_STATUS;
input_report_key(priv->idev, BTN_TOUCH, priv->pen_status); input_report_key(priv->idev, BTN_TOUCH, priv->pen_status);
needs_sync = true; needs_sync = true;
dev_dbg(&priv->pdev->dev, dev_dbg(&priv->pdev->dev,
...@@ -221,7 +227,7 @@ static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data) ...@@ -221,7 +227,7 @@ static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data)
/* coordinates in FIFO exceed the theshold */ /* coordinates in FIFO exceed the theshold */
if (intr_status & TS_FIFO_INTR_MASK) { if (intr_status & TS_FIFO_INTR_MASK) {
for (i = 0; i < priv->cfg_params.fifo_threshold; i++) { 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) if (raw_coordinate == INVALID_COORD)
continue; continue;
...@@ -239,7 +245,7 @@ static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data) ...@@ -239,7 +245,7 @@ static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data)
x = (x >> 4) & 0x0FFF; x = (x >> 4) & 0x0FFF;
y = (y >> 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) if (priv->cfg_params.invert_x)
x = priv->cfg_params.max_x - x; x = priv->cfg_params.max_x - x;
...@@ -262,9 +268,10 @@ static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data) ...@@ -262,9 +268,10 @@ static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data)
static int iproc_ts_start(struct input_dev *idev) static int iproc_ts_start(struct input_dev *idev)
{ {
struct iproc_ts_priv *priv = input_get_drvdata(idev);
u32 val; u32 val;
u32 mask;
int error; int error;
struct iproc_ts_priv *priv = input_get_drvdata(idev);
/* Enable clock */ /* Enable clock */
error = clk_prepare_enable(priv->tsc_clk); error = clk_prepare_enable(priv->tsc_clk);
...@@ -279,9 +286,10 @@ static int iproc_ts_start(struct input_dev *idev) ...@@ -279,9 +286,10 @@ static int iproc_ts_start(struct input_dev *idev)
* FIFO reaches the int_th value, and pen event(up/down) * FIFO reaches the int_th value, and pen event(up/down)
*/ */
val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK; 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 */ /* Initialize control reg1 */
val = 0; val = 0;
...@@ -289,26 +297,23 @@ static int iproc_ts_start(struct input_dev *idev) ...@@ -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.debounce_timeout << DEBOUNCE_TIMEOUT_SHIFT;
val |= priv->cfg_params.settling_timeout << SETTLING_TIMEOUT_SHIFT; val |= priv->cfg_params.settling_timeout << SETTLING_TIMEOUT_SHIFT;
val |= priv->cfg_params.touch_timeout << TOUCH_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 */ /* Try to clear all interrupt status */
val = readl(priv->regs + INTERRUPT_STATUS); val = TS_FIFO_INTR_MASK | TS_PEN_INTR_MASK;
val |= TS_FIFO_INTR_MASK | TS_PEN_INTR_MASK; regmap_update_bits(priv->regmap, INTERRUPT_STATUS, val, val);
writel(val, priv->regs + INTERRUPT_STATUS);
/* Initialize control reg2 */ /* Initialize control reg2 */
val = readl(priv->regs + REGCTL2); val = TS_CONTROLLER_EN_BIT | TS_WIRE_MODE_BIT;
val |= TS_CONTROLLER_EN_BIT | TS_WIRE_MODE_BIT;
val &= ~TS_CONTROLLER_AVGDATA_MASK;
val |= priv->cfg_params.average_data << TS_CONTROLLER_AVGDATA_SHIFT; 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_ADC | /* PWR up ADC */
TS_CONTROLLER_PWR_BGP | /* PWR up BGP */ TS_CONTROLLER_PWR_BGP | /* PWR up BGP */
TS_CONTROLLER_PWR_TS); /* PWR up TS */ TS_CONTROLLER_PWR_TS); /* PWR up TS */
mask |= val;
writel(val, priv->regs + REGCTL2); regmap_update_bits(priv->regmap, REGCTL2, mask, val);
ts_reg_dump(priv); ts_reg_dump(priv);
...@@ -320,12 +325,17 @@ static void iproc_ts_stop(struct input_dev *dev) ...@@ -320,12 +325,17 @@ static void iproc_ts_stop(struct input_dev *dev)
u32 val; u32 val;
struct iproc_ts_priv *priv = input_get_drvdata(dev); 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 */ /* Only power down touch screen controller */
val = readl(priv->regs + REGCTL2); val = TS_CONTROLLER_PWR_TS;
val |= TS_CONTROLLER_PWR_TS; regmap_update_bits(priv->regmap, REGCTL2, val, val);
writel(val, priv->regs + REGCTL2);
clk_disable(priv->tsc_clk); clk_disable(priv->tsc_clk);
} }
...@@ -414,7 +424,6 @@ static int iproc_ts_probe(struct platform_device *pdev) ...@@ -414,7 +424,6 @@ static int iproc_ts_probe(struct platform_device *pdev)
{ {
struct iproc_ts_priv *priv; struct iproc_ts_priv *priv;
struct input_dev *idev; struct input_dev *idev;
struct resource *res;
int irq; int irq;
int error; int error;
...@@ -422,12 +431,12 @@ static int iproc_ts_probe(struct platform_device *pdev) ...@@ -422,12 +431,12 @@ static int iproc_ts_probe(struct platform_device *pdev)
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
/* touchscreen controller memory mapped regs */ /* touchscreen controller memory mapped regs via syscon*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
priv->regs = devm_ioremap_resource(&pdev->dev, res); "ts_syscon");
if (IS_ERR(priv->regs)) { if (IS_ERR(priv->regmap)) {
error = PTR_ERR(priv->regs); error = PTR_ERR(priv->regmap);
dev_err(&pdev->dev, "unable to map I/O memory: %d\n", error); dev_err(&pdev->dev, "unable to map I/O memory:%d\n", error);
return error; return error;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment