Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Raveendra Padasalagi <raveendra.padasalagi@broadcom.com>
Reviewed-by: Ray Jui <ray.jui@broadcom.com>
Reviewed-by: Scott Branden <scott.branden@broadcom.com>
Acked-by: Rob Herring <robh@kernel.org>
Acked-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

authored by

Raveendra Padasalagi and committed by
Dmitry Torokhov
74813ceb 20aa787e

+68 -41
+16 -5
Documentation/devicetree/bindings/input/touchscreen/brcm,iproc-touchscreen.txt
··· 2 2 3 3 Required properties: 4 4 - compatible: must be "brcm,iproc-touchscreen" 5 - - reg: physical base address of the controller and length of memory mapped 6 - region. 5 + - ts_syscon: handler of syscon node defining physical base 6 + address of the controller and length of memory mapped region. 7 + If this property is selected please make sure MFD_SYSCON config 8 + is enabled in the defconfig file. 7 9 - clocks: The clock provided by the SOC to driver the tsc 8 10 - clock-name: name for the clock 9 11 - interrupts: The touchscreen controller's interrupt 12 + - address-cells: Specify the number of u32 entries needed in child nodes. 13 + Should set to 1. 14 + - size-cells: Specify number of u32 entries needed to specify child nodes size 15 + in reg property. Should set to 1. 10 16 11 17 Optional properties: 12 18 - scanning_period: Time between scans. Each step is 1024 us. Valid 1-256. ··· 59 53 - touchscreen-inverted-x: X axis is inverted (boolean) 60 54 - touchscreen-inverted-y: Y axis is inverted (boolean) 61 55 62 - Example: 56 + Example: An example of touchscreen node 63 57 64 - touchscreen: tsc@0x180A6000 { 58 + ts_adc_syscon: ts_adc_syscon@180a6000 { 59 + compatible = "brcm,iproc-ts-adc-syscon","syscon"; 60 + reg = <0x180a6000 0xc30>; 61 + }; 62 + 63 + touchscreen: touchscreen@180A6000 { 65 64 compatible = "brcm,iproc-touchscreen"; 66 65 #address-cells = <1>; 67 66 #size-cells = <1>; 68 - reg = <0x180A6000 0x40>; 67 + ts_syscon = <&ts_adc_syscon>; 69 68 clocks = <&adc_clk>; 70 69 clock-names = "tsc_clk"; 71 70 interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>;
+9 -2
arch/arm/boot/dts/bcm-cygnus.dtsi
··· 351 351 <&pinctrl 142 10 1>; 352 352 }; 353 353 354 - touchscreen: tsc@180a6000 { 354 + ts_adc_syscon: ts_adc_syscon@180a6000 { 355 + compatible = "brcm,iproc-ts-adc-syscon", "syscon"; 356 + reg = <0x180a6000 0xc30>; 357 + }; 358 + 359 + touchscreen: touchscreen@180a6000 { 355 360 compatible = "brcm,iproc-touchscreen"; 356 - reg = <0x180a6000 0x40>; 361 + #address-cells = <1>; 362 + #size-cells = <1>; 363 + ts_syscon = <&ts_adc_syscon>; 357 364 clocks = <&asiu_clks BCM_CYGNUS_ASIU_ADC_CLK>; 358 365 clock-names = "tsc_clk"; 359 366 interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>;
+43 -34
drivers/input/touchscreen/bcm_iproc_tsc.c
··· 23 23 #include <linux/io.h> 24 24 #include <linux/clk.h> 25 25 #include <linux/serio.h> 26 + #include <linux/mfd/syscon.h> 27 + #include <linux/regmap.h> 26 28 27 29 #define IPROC_TS_NAME "iproc-ts" 28 30 ··· 90 88 #define TS_WIRE_MODE_BIT BIT(1) 91 89 92 90 #define dbg_reg(dev, priv, reg) \ 93 - dev_dbg(dev, "%20s= 0x%08x\n", #reg, readl((priv)->regs + reg)) 91 + do { \ 92 + u32 val; \ 93 + regmap_read(priv->regmap, reg, &val); \ 94 + dev_dbg(dev, "%20s= 0x%08x\n", #reg, val); \ 95 + } while (0) 94 96 95 97 struct tsc_param { 96 98 /* Each step is 1024 us. Valid 1-256 */ ··· 147 141 struct platform_device *pdev; 148 142 struct input_dev *idev; 149 143 150 - void __iomem *regs; 144 + struct regmap *regmap; 151 145 struct clk *tsc_clk; 152 146 153 147 int pen_status; ··· 202 196 int i; 203 197 bool needs_sync = false; 204 198 205 - intr_status = readl(priv->regs + INTERRUPT_STATUS); 199 + regmap_read(priv->regmap, INTERRUPT_STATUS, &intr_status); 206 200 intr_status &= TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK; 207 201 if (intr_status == 0) 208 202 return IRQ_NONE; 209 203 210 204 /* Clear all interrupt status bits, write-1-clear */ 211 - writel(intr_status, priv->regs + INTERRUPT_STATUS); 212 - 205 + regmap_write(priv->regmap, INTERRUPT_STATUS, intr_status); 213 206 /* Pen up/down */ 214 207 if (intr_status & TS_PEN_INTR_MASK) { 215 - if (readl(priv->regs + CONTROLLER_STATUS) & TS_PEN_DOWN) 208 + regmap_read(priv->regmap, CONTROLLER_STATUS, &priv->pen_status); 209 + if (priv->pen_status & TS_PEN_DOWN) 216 210 priv->pen_status = PEN_DOWN_STATUS; 217 211 else 218 212 priv->pen_status = PEN_UP_STATUS; 219 213 220 - input_report_key(priv->idev, BTN_TOUCH, priv->pen_status); 214 + input_report_key(priv->idev, BTN_TOUCH, priv->pen_status); 221 215 needs_sync = true; 222 216 223 217 dev_dbg(&priv->pdev->dev, ··· 227 221 /* coordinates in FIFO exceed the theshold */ 228 222 if (intr_status & TS_FIFO_INTR_MASK) { 229 223 for (i = 0; i < priv->cfg_params.fifo_threshold; i++) { 230 - raw_coordinate = readl(priv->regs + FIFO_DATA); 224 + regmap_read(priv->regmap, FIFO_DATA, &raw_coordinate); 231 225 if (raw_coordinate == INVALID_COORD) 232 226 continue; 233 227 ··· 245 239 x = (x >> 4) & 0x0FFF; 246 240 y = (y >> 4) & 0x0FFF; 247 241 248 - /* adjust x y according to lcd tsc mount angle */ 242 + /* Adjust x y according to LCD tsc mount angle. */ 249 243 if (priv->cfg_params.invert_x) 250 244 x = priv->cfg_params.max_x - x; 251 245 ··· 268 262 269 263 static int iproc_ts_start(struct input_dev *idev) 270 264 { 271 - struct iproc_ts_priv *priv = input_get_drvdata(idev); 272 265 u32 val; 266 + u32 mask; 273 267 int error; 268 + struct iproc_ts_priv *priv = input_get_drvdata(idev); 274 269 275 270 /* Enable clock */ 276 271 error = clk_prepare_enable(priv->tsc_clk); ··· 286 279 * FIFO reaches the int_th value, and pen event(up/down) 287 280 */ 288 281 val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK; 289 - writel(val, priv->regs + INTERRUPT_MASK); 282 + regmap_update_bits(priv->regmap, INTERRUPT_MASK, val, val); 290 283 291 - writel(priv->cfg_params.fifo_threshold, priv->regs + INTERRUPT_THRES); 284 + val = priv->cfg_params.fifo_threshold; 285 + regmap_write(priv->regmap, INTERRUPT_THRES, val); 292 286 293 287 /* Initialize control reg1 */ 294 288 val = 0; ··· 297 289 val |= priv->cfg_params.debounce_timeout << DEBOUNCE_TIMEOUT_SHIFT; 298 290 val |= priv->cfg_params.settling_timeout << SETTLING_TIMEOUT_SHIFT; 299 291 val |= priv->cfg_params.touch_timeout << TOUCH_TIMEOUT_SHIFT; 300 - writel(val, priv->regs + REGCTL1); 292 + regmap_write(priv->regmap, REGCTL1, val); 301 293 302 294 /* Try to clear all interrupt status */ 303 - val = readl(priv->regs + INTERRUPT_STATUS); 304 - val |= TS_FIFO_INTR_MASK | TS_PEN_INTR_MASK; 305 - writel(val, priv->regs + INTERRUPT_STATUS); 295 + val = TS_FIFO_INTR_MASK | TS_PEN_INTR_MASK; 296 + regmap_update_bits(priv->regmap, INTERRUPT_STATUS, val, val); 306 297 307 298 /* Initialize control reg2 */ 308 - val = readl(priv->regs + REGCTL2); 309 - val |= TS_CONTROLLER_EN_BIT | TS_WIRE_MODE_BIT; 310 - 311 - val &= ~TS_CONTROLLER_AVGDATA_MASK; 299 + val = TS_CONTROLLER_EN_BIT | TS_WIRE_MODE_BIT; 312 300 val |= priv->cfg_params.average_data << TS_CONTROLLER_AVGDATA_SHIFT; 313 301 314 - val &= ~(TS_CONTROLLER_PWR_LDO | /* PWR up LDO */ 302 + mask = (TS_CONTROLLER_AVGDATA_MASK); 303 + mask |= (TS_CONTROLLER_PWR_LDO | /* PWR up LDO */ 315 304 TS_CONTROLLER_PWR_ADC | /* PWR up ADC */ 316 305 TS_CONTROLLER_PWR_BGP | /* PWR up BGP */ 317 306 TS_CONTROLLER_PWR_TS); /* PWR up TS */ 318 - 319 - writel(val, priv->regs + REGCTL2); 307 + mask |= val; 308 + regmap_update_bits(priv->regmap, REGCTL2, mask, val); 320 309 321 310 ts_reg_dump(priv); 322 311 ··· 325 320 u32 val; 326 321 struct iproc_ts_priv *priv = input_get_drvdata(dev); 327 322 328 - writel(0, priv->regs + INTERRUPT_MASK); /* Disable all interrupts */ 323 + /* 324 + * Disable FIFO int_th and pen event(up/down)Interrupts only 325 + * as the interrupt mask register is shared between ADC, TS and 326 + * flextimer. 327 + */ 328 + val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK; 329 + regmap_update_bits(priv->regmap, INTERRUPT_MASK, val, 0); 329 330 330 331 /* Only power down touch screen controller */ 331 - val = readl(priv->regs + REGCTL2); 332 - val |= TS_CONTROLLER_PWR_TS; 333 - writel(val, priv->regs + REGCTL2); 332 + val = TS_CONTROLLER_PWR_TS; 333 + regmap_update_bits(priv->regmap, REGCTL2, val, val); 334 334 335 335 clk_disable(priv->tsc_clk); 336 336 } ··· 424 414 { 425 415 struct iproc_ts_priv *priv; 426 416 struct input_dev *idev; 427 - struct resource *res; 428 417 int irq; 429 418 int error; 430 419 ··· 431 422 if (!priv) 432 423 return -ENOMEM; 433 424 434 - /* touchscreen controller memory mapped regs */ 435 - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 436 - priv->regs = devm_ioremap_resource(&pdev->dev, res); 437 - if (IS_ERR(priv->regs)) { 438 - error = PTR_ERR(priv->regs); 439 - dev_err(&pdev->dev, "unable to map I/O memory: %d\n", error); 425 + /* touchscreen controller memory mapped regs via syscon*/ 426 + priv->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 427 + "ts_syscon"); 428 + if (IS_ERR(priv->regmap)) { 429 + error = PTR_ERR(priv->regmap); 430 + dev_err(&pdev->dev, "unable to map I/O memory:%d\n", error); 440 431 return error; 441 432 } 442 433