root/drivers/gpio/gpio-ts5500.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ts5500_set_mask
  2. ts5500_clear_mask
  3. ts5500_gpio_input
  4. ts5500_gpio_get
  5. ts5500_gpio_output
  6. ts5500_gpio_set
  7. ts5500_gpio_to_irq
  8. ts5500_enable_irq
  9. ts5500_disable_irq
  10. ts5500_dio_probe
  11. ts5500_dio_remove

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Digital I/O driver for Technologic Systems TS-5500
   4  *
   5  * Copyright (c) 2012 Savoir-faire Linux Inc.
   6  *      Vivien Didelot <vivien.didelot@savoirfairelinux.com>
   7  *
   8  * Technologic Systems platforms have pin blocks, exposing several Digital
   9  * Input/Output lines (DIO). This driver aims to support single pin blocks.
  10  * In that sense, the support is not limited to the TS-5500 blocks.
  11  * Actually, the following platforms have DIO support:
  12  *
  13  * TS-5500:
  14  *   Documentation: http://wiki.embeddedarm.com/wiki/TS-5500
  15  *   Blocks: DIO1, DIO2 and LCD port.
  16  *
  17  * TS-5600:
  18  *   Documentation: http://wiki.embeddedarm.com/wiki/TS-5600
  19  *   Blocks: LCD port (identical to TS-5500 LCD).
  20  */
  21 
  22 #include <linux/bitops.h>
  23 #include <linux/gpio/driver.h>
  24 #include <linux/io.h>
  25 #include <linux/module.h>
  26 #include <linux/platform_device.h>
  27 #include <linux/slab.h>
  28 
  29 /* List of supported Technologic Systems platforms DIO blocks */
  30 enum ts5500_blocks { TS5500_DIO1, TS5500_DIO2, TS5500_LCD, TS5600_LCD };
  31 
  32 struct ts5500_priv {
  33         const struct ts5500_dio *pinout;
  34         struct gpio_chip gpio_chip;
  35         spinlock_t lock;
  36         bool strap;
  37         u8 hwirq;
  38 };
  39 
  40 /*
  41  * Hex 7D is used to control several blocks (e.g. DIO2 and LCD port).
  42  * This flag ensures that the region has been requested by this driver.
  43  */
  44 static bool hex7d_reserved;
  45 
  46 /*
  47  * This structure is used to describe capabilities of DIO lines,
  48  * such as available directions and connected interrupt (if any).
  49  */
  50 struct ts5500_dio {
  51         const u8 value_addr;
  52         const u8 value_mask;
  53         const u8 control_addr;
  54         const u8 control_mask;
  55         const bool no_input;
  56         const bool no_output;
  57         const u8 irq;
  58 };
  59 
  60 #define TS5500_DIO_IN_OUT(vaddr, vbit, caddr, cbit)     \
  61         {                                               \
  62                 .value_addr = vaddr,                    \
  63                 .value_mask = BIT(vbit),                \
  64                 .control_addr = caddr,                  \
  65                 .control_mask = BIT(cbit),              \
  66         }
  67 
  68 #define TS5500_DIO_IN(addr, bit)                \
  69         {                                       \
  70                 .value_addr = addr,             \
  71                 .value_mask = BIT(bit),         \
  72                 .no_output = true,              \
  73         }
  74 
  75 #define TS5500_DIO_IN_IRQ(addr, bit, _irq)      \
  76         {                                       \
  77                 .value_addr = addr,             \
  78                 .value_mask = BIT(bit),         \
  79                 .no_output = true,              \
  80                 .irq = _irq,                    \
  81         }
  82 
  83 #define TS5500_DIO_OUT(addr, bit)               \
  84         {                                       \
  85                 .value_addr = addr,             \
  86                 .value_mask = BIT(bit),         \
  87                 .no_input = true,               \
  88         }
  89 
  90 /*
  91  * Input/Output DIO lines are programmed in groups of 4. Their values are
  92  * available through 4 consecutive bits in a value port, whereas the direction
  93  * of these 4 lines is driven by only 1 bit in a control port.
  94  */
  95 #define TS5500_DIO_GROUP(vaddr, vbitfrom, caddr, cbit)          \
  96         TS5500_DIO_IN_OUT(vaddr, vbitfrom + 0, caddr, cbit),    \
  97         TS5500_DIO_IN_OUT(vaddr, vbitfrom + 1, caddr, cbit),    \
  98         TS5500_DIO_IN_OUT(vaddr, vbitfrom + 2, caddr, cbit),    \
  99         TS5500_DIO_IN_OUT(vaddr, vbitfrom + 3, caddr, cbit)
 100 
 101 /*
 102  * TS-5500 DIO1 block
 103  *
 104  *  value    control  dir    hw
 105  *  addr bit addr bit in out irq name     pin offset
 106  *
 107  *  0x7b  0  0x7a  0  x   x      DIO1_0   1   0
 108  *  0x7b  1  0x7a  0  x   x      DIO1_1   3   1
 109  *  0x7b  2  0x7a  0  x   x      DIO1_2   5   2
 110  *  0x7b  3  0x7a  0  x   x      DIO1_3   7   3
 111  *  0x7b  4  0x7a  1  x   x      DIO1_4   9   4
 112  *  0x7b  5  0x7a  1  x   x      DIO1_5   11  5
 113  *  0x7b  6  0x7a  1  x   x      DIO1_6   13  6
 114  *  0x7b  7  0x7a  1  x   x      DIO1_7   15  7
 115  *  0x7c  0  0x7a  5  x   x      DIO1_8   4   8
 116  *  0x7c  1  0x7a  5  x   x      DIO1_9   6   9
 117  *  0x7c  2  0x7a  5  x   x      DIO1_10  8   10
 118  *  0x7c  3  0x7a  5  x   x      DIO1_11  10  11
 119  *  0x7c  4           x          DIO1_12  12  12
 120  *  0x7c  5           x      7   DIO1_13  14  13
 121  */
 122 static const struct ts5500_dio ts5500_dio1[] = {
 123         TS5500_DIO_GROUP(0x7b, 0, 0x7a, 0),
 124         TS5500_DIO_GROUP(0x7b, 4, 0x7a, 1),
 125         TS5500_DIO_GROUP(0x7c, 0, 0x7a, 5),
 126         TS5500_DIO_IN(0x7c, 4),
 127         TS5500_DIO_IN_IRQ(0x7c, 5, 7),
 128 };
 129 
 130 /*
 131  * TS-5500 DIO2 block
 132  *
 133  *  value    control  dir    hw
 134  *  addr bit addr bit in out irq name     pin offset
 135  *
 136  *  0x7e  0  0x7d  0  x   x      DIO2_0   1   0
 137  *  0x7e  1  0x7d  0  x   x      DIO2_1   3   1
 138  *  0x7e  2  0x7d  0  x   x      DIO2_2   5   2
 139  *  0x7e  3  0x7d  0  x   x      DIO2_3   7   3
 140  *  0x7e  4  0x7d  1  x   x      DIO2_4   9   4
 141  *  0x7e  5  0x7d  1  x   x      DIO2_5   11  5
 142  *  0x7e  6  0x7d  1  x   x      DIO2_6   13  6
 143  *  0x7e  7  0x7d  1  x   x      DIO2_7   15  7
 144  *  0x7f  0  0x7d  5  x   x      DIO2_8   4   8
 145  *  0x7f  1  0x7d  5  x   x      DIO2_9   6   9
 146  *  0x7f  2  0x7d  5  x   x      DIO2_10  8   10
 147  *  0x7f  3  0x7d  5  x   x      DIO2_11  10  11
 148  *  0x7f  4           x      6   DIO2_13  14  12
 149  */
 150 static const struct ts5500_dio ts5500_dio2[] = {
 151         TS5500_DIO_GROUP(0x7e, 0, 0x7d, 0),
 152         TS5500_DIO_GROUP(0x7e, 4, 0x7d, 1),
 153         TS5500_DIO_GROUP(0x7f, 0, 0x7d, 5),
 154         TS5500_DIO_IN_IRQ(0x7f, 4, 6),
 155 };
 156 
 157 /*
 158  * TS-5500 LCD port used as DIO block
 159  * TS-5600 LCD port is identical
 160  *
 161  *  value    control  dir    hw
 162  *  addr bit addr bit in out irq name    pin offset
 163  *
 164  *  0x72  0  0x7d  2  x   x      LCD_0   8   0
 165  *  0x72  1  0x7d  2  x   x      LCD_1   7   1
 166  *  0x72  2  0x7d  2  x   x      LCD_2   10  2
 167  *  0x72  3  0x7d  2  x   x      LCD_3   9   3
 168  *  0x72  4  0x7d  3  x   x      LCD_4   12  4
 169  *  0x72  5  0x7d  3  x   x      LCD_5   11  5
 170  *  0x72  6  0x7d  3  x   x      LCD_6   14  6
 171  *  0x72  7  0x7d  3  x   x      LCD_7   13  7
 172  *  0x73  0               x      LCD_EN  5   8
 173  *  0x73  6           x          LCD_WR  6   9
 174  *  0x73  7           x      1   LCD_RS  3   10
 175  */
 176 static const struct ts5500_dio ts5500_lcd[] = {
 177         TS5500_DIO_GROUP(0x72, 0, 0x7d, 2),
 178         TS5500_DIO_GROUP(0x72, 4, 0x7d, 3),
 179         TS5500_DIO_OUT(0x73, 0),
 180         TS5500_DIO_IN(0x73, 6),
 181         TS5500_DIO_IN_IRQ(0x73, 7, 1),
 182 };
 183 
 184 static inline void ts5500_set_mask(u8 mask, u8 addr)
 185 {
 186         u8 val = inb(addr);
 187         val |= mask;
 188         outb(val, addr);
 189 }
 190 
 191 static inline void ts5500_clear_mask(u8 mask, u8 addr)
 192 {
 193         u8 val = inb(addr);
 194         val &= ~mask;
 195         outb(val, addr);
 196 }
 197 
 198 static int ts5500_gpio_input(struct gpio_chip *chip, unsigned offset)
 199 {
 200         struct ts5500_priv *priv = gpiochip_get_data(chip);
 201         const struct ts5500_dio line = priv->pinout[offset];
 202         unsigned long flags;
 203 
 204         if (line.no_input)
 205                 return -ENXIO;
 206 
 207         if (line.no_output)
 208                 return 0;
 209 
 210         spin_lock_irqsave(&priv->lock, flags);
 211         ts5500_clear_mask(line.control_mask, line.control_addr);
 212         spin_unlock_irqrestore(&priv->lock, flags);
 213 
 214         return 0;
 215 }
 216 
 217 static int ts5500_gpio_get(struct gpio_chip *chip, unsigned offset)
 218 {
 219         struct ts5500_priv *priv = gpiochip_get_data(chip);
 220         const struct ts5500_dio line = priv->pinout[offset];
 221 
 222         return !!(inb(line.value_addr) & line.value_mask);
 223 }
 224 
 225 static int ts5500_gpio_output(struct gpio_chip *chip, unsigned offset, int val)
 226 {
 227         struct ts5500_priv *priv = gpiochip_get_data(chip);
 228         const struct ts5500_dio line = priv->pinout[offset];
 229         unsigned long flags;
 230 
 231         if (line.no_output)
 232                 return -ENXIO;
 233 
 234         spin_lock_irqsave(&priv->lock, flags);
 235         if (!line.no_input)
 236                 ts5500_set_mask(line.control_mask, line.control_addr);
 237 
 238         if (val)
 239                 ts5500_set_mask(line.value_mask, line.value_addr);
 240         else
 241                 ts5500_clear_mask(line.value_mask, line.value_addr);
 242         spin_unlock_irqrestore(&priv->lock, flags);
 243 
 244         return 0;
 245 }
 246 
 247 static void ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
 248 {
 249         struct ts5500_priv *priv = gpiochip_get_data(chip);
 250         const struct ts5500_dio line = priv->pinout[offset];
 251         unsigned long flags;
 252 
 253         spin_lock_irqsave(&priv->lock, flags);
 254         if (val)
 255                 ts5500_set_mask(line.value_mask, line.value_addr);
 256         else
 257                 ts5500_clear_mask(line.value_mask, line.value_addr);
 258         spin_unlock_irqrestore(&priv->lock, flags);
 259 }
 260 
 261 static int ts5500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
 262 {
 263         struct ts5500_priv *priv = gpiochip_get_data(chip);
 264         const struct ts5500_dio *block = priv->pinout;
 265         const struct ts5500_dio line = block[offset];
 266 
 267         /* Only one pin is connected to an interrupt */
 268         if (line.irq)
 269                 return line.irq;
 270 
 271         /* As this pin is input-only, we may strap it to another in/out pin */
 272         if (priv->strap)
 273                 return priv->hwirq;
 274 
 275         return -ENXIO;
 276 }
 277 
 278 static int ts5500_enable_irq(struct ts5500_priv *priv)
 279 {
 280         int ret = 0;
 281         unsigned long flags;
 282 
 283         spin_lock_irqsave(&priv->lock, flags);
 284         if (priv->hwirq == 7)
 285                 ts5500_set_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */
 286         else if (priv->hwirq == 6)
 287                 ts5500_set_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */
 288         else if (priv->hwirq == 1)
 289                 ts5500_set_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */
 290         else
 291                 ret = -EINVAL;
 292         spin_unlock_irqrestore(&priv->lock, flags);
 293 
 294         return ret;
 295 }
 296 
 297 static void ts5500_disable_irq(struct ts5500_priv *priv)
 298 {
 299         unsigned long flags;
 300 
 301         spin_lock_irqsave(&priv->lock, flags);
 302         if (priv->hwirq == 7)
 303                 ts5500_clear_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */
 304         else if (priv->hwirq == 6)
 305                 ts5500_clear_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */
 306         else if (priv->hwirq == 1)
 307                 ts5500_clear_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */
 308         else
 309                 dev_err(priv->gpio_chip.parent, "invalid hwirq %d\n",
 310                         priv->hwirq);
 311         spin_unlock_irqrestore(&priv->lock, flags);
 312 }
 313 
 314 static int ts5500_dio_probe(struct platform_device *pdev)
 315 {
 316         enum ts5500_blocks block = platform_get_device_id(pdev)->driver_data;
 317         struct device *dev = &pdev->dev;
 318         const char *name = dev_name(dev);
 319         struct ts5500_priv *priv;
 320         struct resource *res;
 321         unsigned long flags;
 322         int ret;
 323 
 324         res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 325         if (!res) {
 326                 dev_err(dev, "missing IRQ resource\n");
 327                 return -EINVAL;
 328         }
 329 
 330         priv = devm_kzalloc(dev, sizeof(struct ts5500_priv), GFP_KERNEL);
 331         if (!priv)
 332                 return -ENOMEM;
 333 
 334         platform_set_drvdata(pdev, priv);
 335         priv->hwirq = res->start;
 336         spin_lock_init(&priv->lock);
 337 
 338         priv->gpio_chip.owner = THIS_MODULE;
 339         priv->gpio_chip.label = name;
 340         priv->gpio_chip.parent = dev;
 341         priv->gpio_chip.direction_input = ts5500_gpio_input;
 342         priv->gpio_chip.direction_output = ts5500_gpio_output;
 343         priv->gpio_chip.get = ts5500_gpio_get;
 344         priv->gpio_chip.set = ts5500_gpio_set;
 345         priv->gpio_chip.to_irq = ts5500_gpio_to_irq;
 346         priv->gpio_chip.base = -1;
 347 
 348         switch (block) {
 349         case TS5500_DIO1:
 350                 priv->pinout = ts5500_dio1;
 351                 priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio1);
 352 
 353                 if (!devm_request_region(dev, 0x7a, 3, name)) {
 354                         dev_err(dev, "failed to request %s ports\n", name);
 355                         return -EBUSY;
 356                 }
 357                 break;
 358         case TS5500_DIO2:
 359                 priv->pinout = ts5500_dio2;
 360                 priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio2);
 361 
 362                 if (!devm_request_region(dev, 0x7e, 2, name)) {
 363                         dev_err(dev, "failed to request %s ports\n", name);
 364                         return -EBUSY;
 365                 }
 366 
 367                 if (hex7d_reserved)
 368                         break;
 369 
 370                 if (!devm_request_region(dev, 0x7d, 1, name)) {
 371                         dev_err(dev, "failed to request %s 7D\n", name);
 372                         return -EBUSY;
 373                 }
 374 
 375                 hex7d_reserved = true;
 376                 break;
 377         case TS5500_LCD:
 378         case TS5600_LCD:
 379                 priv->pinout = ts5500_lcd;
 380                 priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_lcd);
 381 
 382                 if (!devm_request_region(dev, 0x72, 2, name)) {
 383                         dev_err(dev, "failed to request %s ports\n", name);
 384                         return -EBUSY;
 385                 }
 386 
 387                 if (!hex7d_reserved) {
 388                         if (!devm_request_region(dev, 0x7d, 1, name)) {
 389                                 dev_err(dev, "failed to request %s 7D\n", name);
 390                                 return -EBUSY;
 391                         }
 392 
 393                         hex7d_reserved = true;
 394                 }
 395 
 396                 /* Ensure usage of LCD port as DIO */
 397                 spin_lock_irqsave(&priv->lock, flags);
 398                 ts5500_clear_mask(BIT(4), 0x7d);
 399                 spin_unlock_irqrestore(&priv->lock, flags);
 400                 break;
 401         }
 402 
 403         ret = devm_gpiochip_add_data(dev, &priv->gpio_chip, priv);
 404         if (ret) {
 405                 dev_err(dev, "failed to register the gpio chip\n");
 406                 return ret;
 407         }
 408 
 409         ret = ts5500_enable_irq(priv);
 410         if (ret) {
 411                 dev_err(dev, "invalid interrupt %d\n", priv->hwirq);
 412                 return ret;
 413         }
 414 
 415         return 0;
 416 }
 417 
 418 static int ts5500_dio_remove(struct platform_device *pdev)
 419 {
 420         struct ts5500_priv *priv = platform_get_drvdata(pdev);
 421 
 422         ts5500_disable_irq(priv);
 423 
 424         return 0;
 425 }
 426 
 427 static const struct platform_device_id ts5500_dio_ids[] = {
 428         { "ts5500-dio1", TS5500_DIO1 },
 429         { "ts5500-dio2", TS5500_DIO2 },
 430         { "ts5500-dio-lcd", TS5500_LCD },
 431         { "ts5600-dio-lcd", TS5600_LCD },
 432         { }
 433 };
 434 MODULE_DEVICE_TABLE(platform, ts5500_dio_ids);
 435 
 436 static struct platform_driver ts5500_dio_driver = {
 437         .driver = {
 438                 .name = "ts5500-dio",
 439         },
 440         .probe = ts5500_dio_probe,
 441         .remove = ts5500_dio_remove,
 442         .id_table = ts5500_dio_ids,
 443 };
 444 
 445 module_platform_driver(ts5500_dio_driver);
 446 
 447 MODULE_LICENSE("GPL");
 448 MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>");
 449 MODULE_DESCRIPTION("Technologic Systems TS-5500 Digital I/O driver");

/* [<][>][^][v][top][bottom][index][help] */