Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Technologic Systems TS-5500 Single Board Computer support
0004  *
0005  * Copyright (C) 2013-2014 Savoir-faire Linux Inc.
0006  *  Vivien Didelot <vivien.didelot@savoirfairelinux.com>
0007  *
0008  * This driver registers the Technologic Systems TS-5500 Single Board Computer
0009  * (SBC) and its devices, and exposes information to userspace such as jumpers'
0010  * state or available options. For further information about sysfs entries, see
0011  * Documentation/ABI/testing/sysfs-platform-ts5500.
0012  *
0013  * This code may be extended to support similar x86-based platforms.
0014  * Actually, the TS-5500 and TS-5400 are supported.
0015  */
0016 
0017 #include <linux/delay.h>
0018 #include <linux/io.h>
0019 #include <linux/kernel.h>
0020 #include <linux/leds.h>
0021 #include <linux/init.h>
0022 #include <linux/platform_data/max197.h>
0023 #include <linux/platform_device.h>
0024 #include <linux/slab.h>
0025 
0026 /* Product code register */
0027 #define TS5500_PRODUCT_CODE_ADDR    0x74
0028 #define TS5500_PRODUCT_CODE     0x60    /* TS-5500 product code */
0029 #define TS5400_PRODUCT_CODE     0x40    /* TS-5400 product code */
0030 
0031 /* SRAM/RS-485/ADC options, and RS-485 RTS/Automatic RS-485 flags register */
0032 #define TS5500_SRAM_RS485_ADC_ADDR  0x75
0033 #define TS5500_SRAM         BIT(0)  /* SRAM option */
0034 #define TS5500_RS485            BIT(1)  /* RS-485 option */
0035 #define TS5500_ADC          BIT(2)  /* A/D converter option */
0036 #define TS5500_RS485_RTS        BIT(6)  /* RTS for RS-485 */
0037 #define TS5500_RS485_AUTO       BIT(7)  /* Automatic RS-485 */
0038 
0039 /* External Reset/Industrial Temperature Range options register */
0040 #define TS5500_ERESET_ITR_ADDR      0x76
0041 #define TS5500_ERESET           BIT(0)  /* External Reset option */
0042 #define TS5500_ITR          BIT(1)  /* Indust. Temp. Range option */
0043 
0044 /* LED/Jumpers register */
0045 #define TS5500_LED_JP_ADDR      0x77
0046 #define TS5500_LED          BIT(0)  /* LED flag */
0047 #define TS5500_JP1          BIT(1)  /* Automatic CMOS */
0048 #define TS5500_JP2          BIT(2)  /* Enable Serial Console */
0049 #define TS5500_JP3          BIT(3)  /* Write Enable Drive A */
0050 #define TS5500_JP4          BIT(4)  /* Fast Console (115K baud) */
0051 #define TS5500_JP5          BIT(5)  /* User Jumper */
0052 #define TS5500_JP6          BIT(6)  /* Console on COM1 (req. JP2) */
0053 #define TS5500_JP7          BIT(7)  /* Undocumented (Unused) */
0054 
0055 /* A/D Converter registers */
0056 #define TS5500_ADC_CONV_BUSY_ADDR   0x195   /* Conversion state register */
0057 #define TS5500_ADC_CONV_BUSY        BIT(0)
0058 #define TS5500_ADC_CONV_INIT_LSB_ADDR   0x196   /* Start conv. / LSB register */
0059 #define TS5500_ADC_CONV_MSB_ADDR    0x197   /* MSB register */
0060 #define TS5500_ADC_CONV_DELAY       12  /* usec */
0061 
0062 /**
0063  * struct ts5500_sbc - TS-5500 board description
0064  * @name:   Board model name.
0065  * @id:     Board product ID.
0066  * @sram:   Flag for SRAM option.
0067  * @rs485:  Flag for RS-485 option.
0068  * @adc:    Flag for Analog/Digital converter option.
0069  * @ereset: Flag for External Reset option.
0070  * @itr:    Flag for Industrial Temperature Range option.
0071  * @jumpers:    Bitfield for jumpers' state.
0072  */
0073 struct ts5500_sbc {
0074     const char *name;
0075     int id;
0076     bool    sram;
0077     bool    rs485;
0078     bool    adc;
0079     bool    ereset;
0080     bool    itr;
0081     u8  jumpers;
0082 };
0083 
0084 /* Board signatures in BIOS shadow RAM */
0085 static const struct {
0086     const char * const string;
0087     const ssize_t offset;
0088 } ts5500_signatures[] __initconst = {
0089     { "TS-5x00 AMD Elan", 0xb14 },
0090 };
0091 
0092 static int __init ts5500_check_signature(void)
0093 {
0094     void __iomem *bios;
0095     int i, ret = -ENODEV;
0096 
0097     bios = ioremap(0xf0000, 0x10000);
0098     if (!bios)
0099         return -ENOMEM;
0100 
0101     for (i = 0; i < ARRAY_SIZE(ts5500_signatures); i++) {
0102         if (check_signature(bios + ts5500_signatures[i].offset,
0103                     ts5500_signatures[i].string,
0104                     strlen(ts5500_signatures[i].string))) {
0105             ret = 0;
0106             break;
0107         }
0108     }
0109 
0110     iounmap(bios);
0111     return ret;
0112 }
0113 
0114 static int __init ts5500_detect_config(struct ts5500_sbc *sbc)
0115 {
0116     u8 tmp;
0117     int ret = 0;
0118 
0119     if (!request_region(TS5500_PRODUCT_CODE_ADDR, 4, "ts5500"))
0120         return -EBUSY;
0121 
0122     sbc->id = inb(TS5500_PRODUCT_CODE_ADDR);
0123     if (sbc->id == TS5500_PRODUCT_CODE) {
0124         sbc->name = "TS-5500";
0125     } else if (sbc->id == TS5400_PRODUCT_CODE) {
0126         sbc->name = "TS-5400";
0127     } else {
0128         pr_err("ts5500: unknown product code 0x%x\n", sbc->id);
0129         ret = -ENODEV;
0130         goto cleanup;
0131     }
0132 
0133     tmp = inb(TS5500_SRAM_RS485_ADC_ADDR);
0134     sbc->sram = tmp & TS5500_SRAM;
0135     sbc->rs485 = tmp & TS5500_RS485;
0136     sbc->adc = tmp & TS5500_ADC;
0137 
0138     tmp = inb(TS5500_ERESET_ITR_ADDR);
0139     sbc->ereset = tmp & TS5500_ERESET;
0140     sbc->itr = tmp & TS5500_ITR;
0141 
0142     tmp = inb(TS5500_LED_JP_ADDR);
0143     sbc->jumpers = tmp & ~TS5500_LED;
0144 
0145 cleanup:
0146     release_region(TS5500_PRODUCT_CODE_ADDR, 4);
0147     return ret;
0148 }
0149 
0150 static ssize_t name_show(struct device *dev, struct device_attribute *attr,
0151         char *buf)
0152 {
0153     struct ts5500_sbc *sbc = dev_get_drvdata(dev);
0154 
0155     return sprintf(buf, "%s\n", sbc->name);
0156 }
0157 static DEVICE_ATTR_RO(name);
0158 
0159 static ssize_t id_show(struct device *dev, struct device_attribute *attr,
0160         char *buf)
0161 {
0162     struct ts5500_sbc *sbc = dev_get_drvdata(dev);
0163 
0164     return sprintf(buf, "0x%.2x\n", sbc->id);
0165 }
0166 static DEVICE_ATTR_RO(id);
0167 
0168 static ssize_t jumpers_show(struct device *dev, struct device_attribute *attr,
0169         char *buf)
0170 {
0171     struct ts5500_sbc *sbc = dev_get_drvdata(dev);
0172 
0173     return sprintf(buf, "0x%.2x\n", sbc->jumpers >> 1);
0174 }
0175 static DEVICE_ATTR_RO(jumpers);
0176 
0177 #define TS5500_ATTR_BOOL(_field)                    \
0178     static ssize_t _field##_show(struct device *dev,        \
0179             struct device_attribute *attr, char *buf)   \
0180     {                               \
0181         struct ts5500_sbc *sbc = dev_get_drvdata(dev);      \
0182                                     \
0183         return sprintf(buf, "%d\n", sbc->_field);       \
0184     }                               \
0185     static DEVICE_ATTR_RO(_field)
0186 
0187 TS5500_ATTR_BOOL(sram);
0188 TS5500_ATTR_BOOL(rs485);
0189 TS5500_ATTR_BOOL(adc);
0190 TS5500_ATTR_BOOL(ereset);
0191 TS5500_ATTR_BOOL(itr);
0192 
0193 static struct attribute *ts5500_attributes[] = {
0194     &dev_attr_id.attr,
0195     &dev_attr_name.attr,
0196     &dev_attr_jumpers.attr,
0197     &dev_attr_sram.attr,
0198     &dev_attr_rs485.attr,
0199     &dev_attr_adc.attr,
0200     &dev_attr_ereset.attr,
0201     &dev_attr_itr.attr,
0202     NULL
0203 };
0204 
0205 static const struct attribute_group ts5500_attr_group = {
0206     .attrs = ts5500_attributes,
0207 };
0208 
0209 static struct resource ts5500_dio1_resource[] = {
0210     DEFINE_RES_IRQ_NAMED(7, "DIO1 interrupt"),
0211 };
0212 
0213 static struct platform_device ts5500_dio1_pdev = {
0214     .name = "ts5500-dio1",
0215     .id = -1,
0216     .resource = ts5500_dio1_resource,
0217     .num_resources = 1,
0218 };
0219 
0220 static struct resource ts5500_dio2_resource[] = {
0221     DEFINE_RES_IRQ_NAMED(6, "DIO2 interrupt"),
0222 };
0223 
0224 static struct platform_device ts5500_dio2_pdev = {
0225     .name = "ts5500-dio2",
0226     .id = -1,
0227     .resource = ts5500_dio2_resource,
0228     .num_resources = 1,
0229 };
0230 
0231 static void ts5500_led_set(struct led_classdev *led_cdev,
0232                enum led_brightness brightness)
0233 {
0234     outb(!!brightness, TS5500_LED_JP_ADDR);
0235 }
0236 
0237 static enum led_brightness ts5500_led_get(struct led_classdev *led_cdev)
0238 {
0239     return (inb(TS5500_LED_JP_ADDR) & TS5500_LED) ? LED_FULL : LED_OFF;
0240 }
0241 
0242 static struct led_classdev ts5500_led_cdev = {
0243     .name = "ts5500:green:",
0244     .brightness_set = ts5500_led_set,
0245     .brightness_get = ts5500_led_get,
0246 };
0247 
0248 static int ts5500_adc_convert(u8 ctrl)
0249 {
0250     u8 lsb, msb;
0251 
0252     /* Start conversion (ensure the 3 MSB are set to 0) */
0253     outb(ctrl & 0x1f, TS5500_ADC_CONV_INIT_LSB_ADDR);
0254 
0255     /*
0256      * The platform has CPLD logic driving the A/D converter.
0257      * The conversion must complete within 11 microseconds,
0258      * otherwise we have to re-initiate a conversion.
0259      */
0260     udelay(TS5500_ADC_CONV_DELAY);
0261     if (inb(TS5500_ADC_CONV_BUSY_ADDR) & TS5500_ADC_CONV_BUSY)
0262         return -EBUSY;
0263 
0264     /* Read the raw data */
0265     lsb = inb(TS5500_ADC_CONV_INIT_LSB_ADDR);
0266     msb = inb(TS5500_ADC_CONV_MSB_ADDR);
0267 
0268     return (msb << 8) | lsb;
0269 }
0270 
0271 static struct max197_platform_data ts5500_adc_pdata = {
0272     .convert = ts5500_adc_convert,
0273 };
0274 
0275 static struct platform_device ts5500_adc_pdev = {
0276     .name = "max197",
0277     .id = -1,
0278     .dev = {
0279         .platform_data = &ts5500_adc_pdata,
0280     },
0281 };
0282 
0283 static int __init ts5500_init(void)
0284 {
0285     struct platform_device *pdev;
0286     struct ts5500_sbc *sbc;
0287     int err;
0288 
0289     /*
0290      * There is no DMI available or PCI bridge subvendor info,
0291      * only the BIOS provides a 16-bit identification call.
0292      * It is safer to find a signature in the BIOS shadow RAM.
0293      */
0294     err = ts5500_check_signature();
0295     if (err)
0296         return err;
0297 
0298     pdev = platform_device_register_simple("ts5500", -1, NULL, 0);
0299     if (IS_ERR(pdev))
0300         return PTR_ERR(pdev);
0301 
0302     sbc = devm_kzalloc(&pdev->dev, sizeof(struct ts5500_sbc), GFP_KERNEL);
0303     if (!sbc) {
0304         err = -ENOMEM;
0305         goto error;
0306     }
0307 
0308     err = ts5500_detect_config(sbc);
0309     if (err)
0310         goto error;
0311 
0312     platform_set_drvdata(pdev, sbc);
0313 
0314     err = sysfs_create_group(&pdev->dev.kobj, &ts5500_attr_group);
0315     if (err)
0316         goto error;
0317 
0318     if (sbc->id == TS5500_PRODUCT_CODE) {
0319         ts5500_dio1_pdev.dev.parent = &pdev->dev;
0320         if (platform_device_register(&ts5500_dio1_pdev))
0321             dev_warn(&pdev->dev, "DIO1 block registration failed\n");
0322         ts5500_dio2_pdev.dev.parent = &pdev->dev;
0323         if (platform_device_register(&ts5500_dio2_pdev))
0324             dev_warn(&pdev->dev, "DIO2 block registration failed\n");
0325     }
0326 
0327     if (led_classdev_register(&pdev->dev, &ts5500_led_cdev))
0328         dev_warn(&pdev->dev, "LED registration failed\n");
0329 
0330     if (sbc->adc) {
0331         ts5500_adc_pdev.dev.parent = &pdev->dev;
0332         if (platform_device_register(&ts5500_adc_pdev))
0333             dev_warn(&pdev->dev, "ADC registration failed\n");
0334     }
0335 
0336     return 0;
0337 error:
0338     platform_device_unregister(pdev);
0339     return err;
0340 }
0341 device_initcall(ts5500_init);