Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Digital I/O driver for Technologic Systems TS-5500
0004  *
0005  * Copyright (c) 2012 Savoir-faire Linux Inc.
0006  *  Vivien Didelot <vivien.didelot@savoirfairelinux.com>
0007  *
0008  * Technologic Systems platforms have pin blocks, exposing several Digital
0009  * Input/Output lines (DIO). This driver aims to support single pin blocks.
0010  * In that sense, the support is not limited to the TS-5500 blocks.
0011  * Actually, the following platforms have DIO support:
0012  *
0013  * TS-5500:
0014  *   Documentation: https://docs.embeddedts.com/TS-5500
0015  *   Blocks: DIO1, DIO2 and LCD port.
0016  *
0017  * TS-5600:
0018  *   Documentation: https://docs.embeddedts.com/TS-5600
0019  *   Blocks: LCD port (identical to TS-5500 LCD).
0020  */
0021 
0022 #include <linux/bitops.h>
0023 #include <linux/gpio/driver.h>
0024 #include <linux/io.h>
0025 #include <linux/module.h>
0026 #include <linux/platform_device.h>
0027 #include <linux/slab.h>
0028 
0029 /* List of supported Technologic Systems platforms DIO blocks */
0030 enum ts5500_blocks { TS5500_DIO1, TS5500_DIO2, TS5500_LCD, TS5600_LCD };
0031 
0032 struct ts5500_priv {
0033     const struct ts5500_dio *pinout;
0034     struct gpio_chip gpio_chip;
0035     spinlock_t lock;
0036     bool strap;
0037     u8 hwirq;
0038 };
0039 
0040 /*
0041  * Hex 7D is used to control several blocks (e.g. DIO2 and LCD port).
0042  * This flag ensures that the region has been requested by this driver.
0043  */
0044 static bool hex7d_reserved;
0045 
0046 /*
0047  * This structure is used to describe capabilities of DIO lines,
0048  * such as available directions and connected interrupt (if any).
0049  */
0050 struct ts5500_dio {
0051     const u8 value_addr;
0052     const u8 value_mask;
0053     const u8 control_addr;
0054     const u8 control_mask;
0055     const bool no_input;
0056     const bool no_output;
0057     const u8 irq;
0058 };
0059 
0060 #define TS5500_DIO_IN_OUT(vaddr, vbit, caddr, cbit) \
0061     {                       \
0062         .value_addr = vaddr,            \
0063         .value_mask = BIT(vbit),        \
0064         .control_addr = caddr,          \
0065         .control_mask = BIT(cbit),      \
0066     }
0067 
0068 #define TS5500_DIO_IN(addr, bit)        \
0069     {                   \
0070         .value_addr = addr,     \
0071         .value_mask = BIT(bit),     \
0072         .no_output = true,      \
0073     }
0074 
0075 #define TS5500_DIO_IN_IRQ(addr, bit, _irq)  \
0076     {                   \
0077         .value_addr = addr,     \
0078         .value_mask = BIT(bit),     \
0079         .no_output = true,      \
0080         .irq = _irq,            \
0081     }
0082 
0083 #define TS5500_DIO_OUT(addr, bit)       \
0084     {                   \
0085         .value_addr = addr,     \
0086         .value_mask = BIT(bit),     \
0087         .no_input = true,       \
0088     }
0089 
0090 /*
0091  * Input/Output DIO lines are programmed in groups of 4. Their values are
0092  * available through 4 consecutive bits in a value port, whereas the direction
0093  * of these 4 lines is driven by only 1 bit in a control port.
0094  */
0095 #define TS5500_DIO_GROUP(vaddr, vbitfrom, caddr, cbit)      \
0096     TS5500_DIO_IN_OUT(vaddr, vbitfrom + 0, caddr, cbit),    \
0097     TS5500_DIO_IN_OUT(vaddr, vbitfrom + 1, caddr, cbit),    \
0098     TS5500_DIO_IN_OUT(vaddr, vbitfrom + 2, caddr, cbit),    \
0099     TS5500_DIO_IN_OUT(vaddr, vbitfrom + 3, caddr, cbit)
0100 
0101 /*
0102  * TS-5500 DIO1 block
0103  *
0104  *  value    control  dir    hw
0105  *  addr bit addr bit in out irq name     pin offset
0106  *
0107  *  0x7b  0  0x7a  0  x   x      DIO1_0   1   0
0108  *  0x7b  1  0x7a  0  x   x      DIO1_1   3   1
0109  *  0x7b  2  0x7a  0  x   x      DIO1_2   5   2
0110  *  0x7b  3  0x7a  0  x   x      DIO1_3   7   3
0111  *  0x7b  4  0x7a  1  x   x      DIO1_4   9   4
0112  *  0x7b  5  0x7a  1  x   x      DIO1_5   11  5
0113  *  0x7b  6  0x7a  1  x   x      DIO1_6   13  6
0114  *  0x7b  7  0x7a  1  x   x      DIO1_7   15  7
0115  *  0x7c  0  0x7a  5  x   x      DIO1_8   4   8
0116  *  0x7c  1  0x7a  5  x   x      DIO1_9   6   9
0117  *  0x7c  2  0x7a  5  x   x      DIO1_10  8   10
0118  *  0x7c  3  0x7a  5  x   x      DIO1_11  10  11
0119  *  0x7c  4           x          DIO1_12  12  12
0120  *  0x7c  5           x      7   DIO1_13  14  13
0121  */
0122 static const struct ts5500_dio ts5500_dio1[] = {
0123     TS5500_DIO_GROUP(0x7b, 0, 0x7a, 0),
0124     TS5500_DIO_GROUP(0x7b, 4, 0x7a, 1),
0125     TS5500_DIO_GROUP(0x7c, 0, 0x7a, 5),
0126     TS5500_DIO_IN(0x7c, 4),
0127     TS5500_DIO_IN_IRQ(0x7c, 5, 7),
0128 };
0129 
0130 /*
0131  * TS-5500 DIO2 block
0132  *
0133  *  value    control  dir    hw
0134  *  addr bit addr bit in out irq name     pin offset
0135  *
0136  *  0x7e  0  0x7d  0  x   x      DIO2_0   1   0
0137  *  0x7e  1  0x7d  0  x   x      DIO2_1   3   1
0138  *  0x7e  2  0x7d  0  x   x      DIO2_2   5   2
0139  *  0x7e  3  0x7d  0  x   x      DIO2_3   7   3
0140  *  0x7e  4  0x7d  1  x   x      DIO2_4   9   4
0141  *  0x7e  5  0x7d  1  x   x      DIO2_5   11  5
0142  *  0x7e  6  0x7d  1  x   x      DIO2_6   13  6
0143  *  0x7e  7  0x7d  1  x   x      DIO2_7   15  7
0144  *  0x7f  0  0x7d  5  x   x      DIO2_8   4   8
0145  *  0x7f  1  0x7d  5  x   x      DIO2_9   6   9
0146  *  0x7f  2  0x7d  5  x   x      DIO2_10  8   10
0147  *  0x7f  3  0x7d  5  x   x      DIO2_11  10  11
0148  *  0x7f  4           x      6   DIO2_13  14  12
0149  */
0150 static const struct ts5500_dio ts5500_dio2[] = {
0151     TS5500_DIO_GROUP(0x7e, 0, 0x7d, 0),
0152     TS5500_DIO_GROUP(0x7e, 4, 0x7d, 1),
0153     TS5500_DIO_GROUP(0x7f, 0, 0x7d, 5),
0154     TS5500_DIO_IN_IRQ(0x7f, 4, 6),
0155 };
0156 
0157 /*
0158  * TS-5500 LCD port used as DIO block
0159  * TS-5600 LCD port is identical
0160  *
0161  *  value    control  dir    hw
0162  *  addr bit addr bit in out irq name    pin offset
0163  *
0164  *  0x72  0  0x7d  2  x   x      LCD_0   8   0
0165  *  0x72  1  0x7d  2  x   x      LCD_1   7   1
0166  *  0x72  2  0x7d  2  x   x      LCD_2   10  2
0167  *  0x72  3  0x7d  2  x   x      LCD_3   9   3
0168  *  0x72  4  0x7d  3  x   x      LCD_4   12  4
0169  *  0x72  5  0x7d  3  x   x      LCD_5   11  5
0170  *  0x72  6  0x7d  3  x   x      LCD_6   14  6
0171  *  0x72  7  0x7d  3  x   x      LCD_7   13  7
0172  *  0x73  0               x      LCD_EN  5   8
0173  *  0x73  6           x          LCD_WR  6   9
0174  *  0x73  7           x      1   LCD_RS  3   10
0175  */
0176 static const struct ts5500_dio ts5500_lcd[] = {
0177     TS5500_DIO_GROUP(0x72, 0, 0x7d, 2),
0178     TS5500_DIO_GROUP(0x72, 4, 0x7d, 3),
0179     TS5500_DIO_OUT(0x73, 0),
0180     TS5500_DIO_IN(0x73, 6),
0181     TS5500_DIO_IN_IRQ(0x73, 7, 1),
0182 };
0183 
0184 static inline void ts5500_set_mask(u8 mask, u8 addr)
0185 {
0186     u8 val = inb(addr);
0187     val |= mask;
0188     outb(val, addr);
0189 }
0190 
0191 static inline void ts5500_clear_mask(u8 mask, u8 addr)
0192 {
0193     u8 val = inb(addr);
0194     val &= ~mask;
0195     outb(val, addr);
0196 }
0197 
0198 static int ts5500_gpio_input(struct gpio_chip *chip, unsigned offset)
0199 {
0200     struct ts5500_priv *priv = gpiochip_get_data(chip);
0201     const struct ts5500_dio line = priv->pinout[offset];
0202     unsigned long flags;
0203 
0204     if (line.no_input)
0205         return -ENXIO;
0206 
0207     if (line.no_output)
0208         return 0;
0209 
0210     spin_lock_irqsave(&priv->lock, flags);
0211     ts5500_clear_mask(line.control_mask, line.control_addr);
0212     spin_unlock_irqrestore(&priv->lock, flags);
0213 
0214     return 0;
0215 }
0216 
0217 static int ts5500_gpio_get(struct gpio_chip *chip, unsigned offset)
0218 {
0219     struct ts5500_priv *priv = gpiochip_get_data(chip);
0220     const struct ts5500_dio line = priv->pinout[offset];
0221 
0222     return !!(inb(line.value_addr) & line.value_mask);
0223 }
0224 
0225 static int ts5500_gpio_output(struct gpio_chip *chip, unsigned offset, int val)
0226 {
0227     struct ts5500_priv *priv = gpiochip_get_data(chip);
0228     const struct ts5500_dio line = priv->pinout[offset];
0229     unsigned long flags;
0230 
0231     if (line.no_output)
0232         return -ENXIO;
0233 
0234     spin_lock_irqsave(&priv->lock, flags);
0235     if (!line.no_input)
0236         ts5500_set_mask(line.control_mask, line.control_addr);
0237 
0238     if (val)
0239         ts5500_set_mask(line.value_mask, line.value_addr);
0240     else
0241         ts5500_clear_mask(line.value_mask, line.value_addr);
0242     spin_unlock_irqrestore(&priv->lock, flags);
0243 
0244     return 0;
0245 }
0246 
0247 static void ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
0248 {
0249     struct ts5500_priv *priv = gpiochip_get_data(chip);
0250     const struct ts5500_dio line = priv->pinout[offset];
0251     unsigned long flags;
0252 
0253     spin_lock_irqsave(&priv->lock, flags);
0254     if (val)
0255         ts5500_set_mask(line.value_mask, line.value_addr);
0256     else
0257         ts5500_clear_mask(line.value_mask, line.value_addr);
0258     spin_unlock_irqrestore(&priv->lock, flags);
0259 }
0260 
0261 static int ts5500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
0262 {
0263     struct ts5500_priv *priv = gpiochip_get_data(chip);
0264     const struct ts5500_dio *block = priv->pinout;
0265     const struct ts5500_dio line = block[offset];
0266 
0267     /* Only one pin is connected to an interrupt */
0268     if (line.irq)
0269         return line.irq;
0270 
0271     /* As this pin is input-only, we may strap it to another in/out pin */
0272     if (priv->strap)
0273         return priv->hwirq;
0274 
0275     return -ENXIO;
0276 }
0277 
0278 static int ts5500_enable_irq(struct ts5500_priv *priv)
0279 {
0280     int ret = 0;
0281     unsigned long flags;
0282 
0283     spin_lock_irqsave(&priv->lock, flags);
0284     if (priv->hwirq == 7)
0285         ts5500_set_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */
0286     else if (priv->hwirq == 6)
0287         ts5500_set_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */
0288     else if (priv->hwirq == 1)
0289         ts5500_set_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */
0290     else
0291         ret = -EINVAL;
0292     spin_unlock_irqrestore(&priv->lock, flags);
0293 
0294     return ret;
0295 }
0296 
0297 static void ts5500_disable_irq(struct ts5500_priv *priv)
0298 {
0299     unsigned long flags;
0300 
0301     spin_lock_irqsave(&priv->lock, flags);
0302     if (priv->hwirq == 7)
0303         ts5500_clear_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */
0304     else if (priv->hwirq == 6)
0305         ts5500_clear_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */
0306     else if (priv->hwirq == 1)
0307         ts5500_clear_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */
0308     else
0309         dev_err(priv->gpio_chip.parent, "invalid hwirq %d\n",
0310             priv->hwirq);
0311     spin_unlock_irqrestore(&priv->lock, flags);
0312 }
0313 
0314 static int ts5500_dio_probe(struct platform_device *pdev)
0315 {
0316     enum ts5500_blocks block = platform_get_device_id(pdev)->driver_data;
0317     struct device *dev = &pdev->dev;
0318     const char *name = dev_name(dev);
0319     struct ts5500_priv *priv;
0320     unsigned long flags;
0321     int ret;
0322 
0323     ret = platform_get_irq(pdev, 0);
0324     if (ret < 0)
0325         return ret;
0326 
0327     priv = devm_kzalloc(dev, sizeof(struct ts5500_priv), GFP_KERNEL);
0328     if (!priv)
0329         return -ENOMEM;
0330 
0331     platform_set_drvdata(pdev, priv);
0332     priv->hwirq = ret;
0333     spin_lock_init(&priv->lock);
0334 
0335     priv->gpio_chip.owner = THIS_MODULE;
0336     priv->gpio_chip.label = name;
0337     priv->gpio_chip.parent = dev;
0338     priv->gpio_chip.direction_input = ts5500_gpio_input;
0339     priv->gpio_chip.direction_output = ts5500_gpio_output;
0340     priv->gpio_chip.get = ts5500_gpio_get;
0341     priv->gpio_chip.set = ts5500_gpio_set;
0342     priv->gpio_chip.to_irq = ts5500_gpio_to_irq;
0343     priv->gpio_chip.base = -1;
0344 
0345     switch (block) {
0346     case TS5500_DIO1:
0347         priv->pinout = ts5500_dio1;
0348         priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio1);
0349 
0350         if (!devm_request_region(dev, 0x7a, 3, name)) {
0351             dev_err(dev, "failed to request %s ports\n", name);
0352             return -EBUSY;
0353         }
0354         break;
0355     case TS5500_DIO2:
0356         priv->pinout = ts5500_dio2;
0357         priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio2);
0358 
0359         if (!devm_request_region(dev, 0x7e, 2, name)) {
0360             dev_err(dev, "failed to request %s ports\n", name);
0361             return -EBUSY;
0362         }
0363 
0364         if (hex7d_reserved)
0365             break;
0366 
0367         if (!devm_request_region(dev, 0x7d, 1, name)) {
0368             dev_err(dev, "failed to request %s 7D\n", name);
0369             return -EBUSY;
0370         }
0371 
0372         hex7d_reserved = true;
0373         break;
0374     case TS5500_LCD:
0375     case TS5600_LCD:
0376         priv->pinout = ts5500_lcd;
0377         priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_lcd);
0378 
0379         if (!devm_request_region(dev, 0x72, 2, name)) {
0380             dev_err(dev, "failed to request %s ports\n", name);
0381             return -EBUSY;
0382         }
0383 
0384         if (!hex7d_reserved) {
0385             if (!devm_request_region(dev, 0x7d, 1, name)) {
0386                 dev_err(dev, "failed to request %s 7D\n", name);
0387                 return -EBUSY;
0388             }
0389 
0390             hex7d_reserved = true;
0391         }
0392 
0393         /* Ensure usage of LCD port as DIO */
0394         spin_lock_irqsave(&priv->lock, flags);
0395         ts5500_clear_mask(BIT(4), 0x7d);
0396         spin_unlock_irqrestore(&priv->lock, flags);
0397         break;
0398     }
0399 
0400     ret = devm_gpiochip_add_data(dev, &priv->gpio_chip, priv);
0401     if (ret) {
0402         dev_err(dev, "failed to register the gpio chip\n");
0403         return ret;
0404     }
0405 
0406     ret = ts5500_enable_irq(priv);
0407     if (ret) {
0408         dev_err(dev, "invalid interrupt %d\n", priv->hwirq);
0409         return ret;
0410     }
0411 
0412     return 0;
0413 }
0414 
0415 static int ts5500_dio_remove(struct platform_device *pdev)
0416 {
0417     struct ts5500_priv *priv = platform_get_drvdata(pdev);
0418 
0419     ts5500_disable_irq(priv);
0420 
0421     return 0;
0422 }
0423 
0424 static const struct platform_device_id ts5500_dio_ids[] = {
0425     { "ts5500-dio1", TS5500_DIO1 },
0426     { "ts5500-dio2", TS5500_DIO2 },
0427     { "ts5500-dio-lcd", TS5500_LCD },
0428     { "ts5600-dio-lcd", TS5600_LCD },
0429     { }
0430 };
0431 MODULE_DEVICE_TABLE(platform, ts5500_dio_ids);
0432 
0433 static struct platform_driver ts5500_dio_driver = {
0434     .driver = {
0435         .name = "ts5500-dio",
0436     },
0437     .probe = ts5500_dio_probe,
0438     .remove = ts5500_dio_remove,
0439     .id_table = ts5500_dio_ids,
0440 };
0441 
0442 module_platform_driver(ts5500_dio_driver);
0443 
0444 MODULE_LICENSE("GPL");
0445 MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>");
0446 MODULE_DESCRIPTION("Technologic Systems TS-5500 Digital I/O driver");