root/arch/x86/platform/ts5500/ts5500.c

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

DEFINITIONS

This source file includes following definitions.
  1. ts5500_check_signature
  2. ts5500_detect_config
  3. name_show
  4. id_show
  5. jumpers_show
  6. ts5500_led_set
  7. ts5500_led_get
  8. ts5500_adc_convert
  9. ts5500_init

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Technologic Systems TS-5500 Single Board Computer support
   4  *
   5  * Copyright (C) 2013-2014 Savoir-faire Linux Inc.
   6  *      Vivien Didelot <vivien.didelot@savoirfairelinux.com>
   7  *
   8  * This driver registers the Technologic Systems TS-5500 Single Board Computer
   9  * (SBC) and its devices, and exposes information to userspace such as jumpers'
  10  * state or available options. For further information about sysfs entries, see
  11  * Documentation/ABI/testing/sysfs-platform-ts5500.
  12  *
  13  * This code may be extended to support similar x86-based platforms.
  14  * Actually, the TS-5500 and TS-5400 are supported.
  15  */
  16 
  17 #include <linux/delay.h>
  18 #include <linux/io.h>
  19 #include <linux/kernel.h>
  20 #include <linux/leds.h>
  21 #include <linux/init.h>
  22 #include <linux/platform_data/max197.h>
  23 #include <linux/platform_device.h>
  24 #include <linux/slab.h>
  25 
  26 /* Product code register */
  27 #define TS5500_PRODUCT_CODE_ADDR        0x74
  28 #define TS5500_PRODUCT_CODE             0x60    /* TS-5500 product code */
  29 #define TS5400_PRODUCT_CODE             0x40    /* TS-5400 product code */
  30 
  31 /* SRAM/RS-485/ADC options, and RS-485 RTS/Automatic RS-485 flags register */
  32 #define TS5500_SRAM_RS485_ADC_ADDR      0x75
  33 #define TS5500_SRAM                     BIT(0)  /* SRAM option */
  34 #define TS5500_RS485                    BIT(1)  /* RS-485 option */
  35 #define TS5500_ADC                      BIT(2)  /* A/D converter option */
  36 #define TS5500_RS485_RTS                BIT(6)  /* RTS for RS-485 */
  37 #define TS5500_RS485_AUTO               BIT(7)  /* Automatic RS-485 */
  38 
  39 /* External Reset/Industrial Temperature Range options register */
  40 #define TS5500_ERESET_ITR_ADDR          0x76
  41 #define TS5500_ERESET                   BIT(0)  /* External Reset option */
  42 #define TS5500_ITR                      BIT(1)  /* Indust. Temp. Range option */
  43 
  44 /* LED/Jumpers register */
  45 #define TS5500_LED_JP_ADDR              0x77
  46 #define TS5500_LED                      BIT(0)  /* LED flag */
  47 #define TS5500_JP1                      BIT(1)  /* Automatic CMOS */
  48 #define TS5500_JP2                      BIT(2)  /* Enable Serial Console */
  49 #define TS5500_JP3                      BIT(3)  /* Write Enable Drive A */
  50 #define TS5500_JP4                      BIT(4)  /* Fast Console (115K baud) */
  51 #define TS5500_JP5                      BIT(5)  /* User Jumper */
  52 #define TS5500_JP6                      BIT(6)  /* Console on COM1 (req. JP2) */
  53 #define TS5500_JP7                      BIT(7)  /* Undocumented (Unused) */
  54 
  55 /* A/D Converter registers */
  56 #define TS5500_ADC_CONV_BUSY_ADDR       0x195   /* Conversion state register */
  57 #define TS5500_ADC_CONV_BUSY            BIT(0)
  58 #define TS5500_ADC_CONV_INIT_LSB_ADDR   0x196   /* Start conv. / LSB register */
  59 #define TS5500_ADC_CONV_MSB_ADDR        0x197   /* MSB register */
  60 #define TS5500_ADC_CONV_DELAY           12      /* usec */
  61 
  62 /**
  63  * struct ts5500_sbc - TS-5500 board description
  64  * @name:       Board model name.
  65  * @id:         Board product ID.
  66  * @sram:       Flag for SRAM option.
  67  * @rs485:      Flag for RS-485 option.
  68  * @adc:        Flag for Analog/Digital converter option.
  69  * @ereset:     Flag for External Reset option.
  70  * @itr:        Flag for Industrial Temperature Range option.
  71  * @jumpers:    Bitfield for jumpers' state.
  72  */
  73 struct ts5500_sbc {
  74         const char *name;
  75         int     id;
  76         bool    sram;
  77         bool    rs485;
  78         bool    adc;
  79         bool    ereset;
  80         bool    itr;
  81         u8      jumpers;
  82 };
  83 
  84 /* Board signatures in BIOS shadow RAM */
  85 static const struct {
  86         const char * const string;
  87         const ssize_t offset;
  88 } ts5500_signatures[] __initconst = {
  89         { "TS-5x00 AMD Elan", 0xb14 },
  90 };
  91 
  92 static int __init ts5500_check_signature(void)
  93 {
  94         void __iomem *bios;
  95         int i, ret = -ENODEV;
  96 
  97         bios = ioremap(0xf0000, 0x10000);
  98         if (!bios)
  99                 return -ENOMEM;
 100 
 101         for (i = 0; i < ARRAY_SIZE(ts5500_signatures); i++) {
 102                 if (check_signature(bios + ts5500_signatures[i].offset,
 103                                     ts5500_signatures[i].string,
 104                                     strlen(ts5500_signatures[i].string))) {
 105                         ret = 0;
 106                         break;
 107                 }
 108         }
 109 
 110         iounmap(bios);
 111         return ret;
 112 }
 113 
 114 static int __init ts5500_detect_config(struct ts5500_sbc *sbc)
 115 {
 116         u8 tmp;
 117         int ret = 0;
 118 
 119         if (!request_region(TS5500_PRODUCT_CODE_ADDR, 4, "ts5500"))
 120                 return -EBUSY;
 121 
 122         sbc->id = inb(TS5500_PRODUCT_CODE_ADDR);
 123         if (sbc->id == TS5500_PRODUCT_CODE) {
 124                 sbc->name = "TS-5500";
 125         } else if (sbc->id == TS5400_PRODUCT_CODE) {
 126                 sbc->name = "TS-5400";
 127         } else {
 128                 pr_err("ts5500: unknown product code 0x%x\n", sbc->id);
 129                 ret = -ENODEV;
 130                 goto cleanup;
 131         }
 132 
 133         tmp = inb(TS5500_SRAM_RS485_ADC_ADDR);
 134         sbc->sram = tmp & TS5500_SRAM;
 135         sbc->rs485 = tmp & TS5500_RS485;
 136         sbc->adc = tmp & TS5500_ADC;
 137 
 138         tmp = inb(TS5500_ERESET_ITR_ADDR);
 139         sbc->ereset = tmp & TS5500_ERESET;
 140         sbc->itr = tmp & TS5500_ITR;
 141 
 142         tmp = inb(TS5500_LED_JP_ADDR);
 143         sbc->jumpers = tmp & ~TS5500_LED;
 144 
 145 cleanup:
 146         release_region(TS5500_PRODUCT_CODE_ADDR, 4);
 147         return ret;
 148 }
 149 
 150 static ssize_t name_show(struct device *dev, struct device_attribute *attr,
 151                 char *buf)
 152 {
 153         struct ts5500_sbc *sbc = dev_get_drvdata(dev);
 154 
 155         return sprintf(buf, "%s\n", sbc->name);
 156 }
 157 static DEVICE_ATTR_RO(name);
 158 
 159 static ssize_t id_show(struct device *dev, struct device_attribute *attr,
 160                 char *buf)
 161 {
 162         struct ts5500_sbc *sbc = dev_get_drvdata(dev);
 163 
 164         return sprintf(buf, "0x%.2x\n", sbc->id);
 165 }
 166 static DEVICE_ATTR_RO(id);
 167 
 168 static ssize_t jumpers_show(struct device *dev, struct device_attribute *attr,
 169                 char *buf)
 170 {
 171         struct ts5500_sbc *sbc = dev_get_drvdata(dev);
 172 
 173         return sprintf(buf, "0x%.2x\n", sbc->jumpers >> 1);
 174 }
 175 static DEVICE_ATTR_RO(jumpers);
 176 
 177 #define TS5500_ATTR_BOOL(_field)                                        \
 178         static ssize_t _field##_show(struct device *dev,                \
 179                         struct device_attribute *attr, char *buf)       \
 180         {                                                               \
 181                 struct ts5500_sbc *sbc = dev_get_drvdata(dev);          \
 182                                                                         \
 183                 return sprintf(buf, "%d\n", sbc->_field);               \
 184         }                                                               \
 185         static DEVICE_ATTR_RO(_field)
 186 
 187 TS5500_ATTR_BOOL(sram);
 188 TS5500_ATTR_BOOL(rs485);
 189 TS5500_ATTR_BOOL(adc);
 190 TS5500_ATTR_BOOL(ereset);
 191 TS5500_ATTR_BOOL(itr);
 192 
 193 static struct attribute *ts5500_attributes[] = {
 194         &dev_attr_id.attr,
 195         &dev_attr_name.attr,
 196         &dev_attr_jumpers.attr,
 197         &dev_attr_sram.attr,
 198         &dev_attr_rs485.attr,
 199         &dev_attr_adc.attr,
 200         &dev_attr_ereset.attr,
 201         &dev_attr_itr.attr,
 202         NULL
 203 };
 204 
 205 static const struct attribute_group ts5500_attr_group = {
 206         .attrs = ts5500_attributes,
 207 };
 208 
 209 static struct resource ts5500_dio1_resource[] = {
 210         DEFINE_RES_IRQ_NAMED(7, "DIO1 interrupt"),
 211 };
 212 
 213 static struct platform_device ts5500_dio1_pdev = {
 214         .name = "ts5500-dio1",
 215         .id = -1,
 216         .resource = ts5500_dio1_resource,
 217         .num_resources = 1,
 218 };
 219 
 220 static struct resource ts5500_dio2_resource[] = {
 221         DEFINE_RES_IRQ_NAMED(6, "DIO2 interrupt"),
 222 };
 223 
 224 static struct platform_device ts5500_dio2_pdev = {
 225         .name = "ts5500-dio2",
 226         .id = -1,
 227         .resource = ts5500_dio2_resource,
 228         .num_resources = 1,
 229 };
 230 
 231 static void ts5500_led_set(struct led_classdev *led_cdev,
 232                            enum led_brightness brightness)
 233 {
 234         outb(!!brightness, TS5500_LED_JP_ADDR);
 235 }
 236 
 237 static enum led_brightness ts5500_led_get(struct led_classdev *led_cdev)
 238 {
 239         return (inb(TS5500_LED_JP_ADDR) & TS5500_LED) ? LED_FULL : LED_OFF;
 240 }
 241 
 242 static struct led_classdev ts5500_led_cdev = {
 243         .name = "ts5500:green:",
 244         .brightness_set = ts5500_led_set,
 245         .brightness_get = ts5500_led_get,
 246 };
 247 
 248 static int ts5500_adc_convert(u8 ctrl)
 249 {
 250         u8 lsb, msb;
 251 
 252         /* Start conversion (ensure the 3 MSB are set to 0) */
 253         outb(ctrl & 0x1f, TS5500_ADC_CONV_INIT_LSB_ADDR);
 254 
 255         /*
 256          * The platform has CPLD logic driving the A/D converter.
 257          * The conversion must complete within 11 microseconds,
 258          * otherwise we have to re-initiate a conversion.
 259          */
 260         udelay(TS5500_ADC_CONV_DELAY);
 261         if (inb(TS5500_ADC_CONV_BUSY_ADDR) & TS5500_ADC_CONV_BUSY)
 262                 return -EBUSY;
 263 
 264         /* Read the raw data */
 265         lsb = inb(TS5500_ADC_CONV_INIT_LSB_ADDR);
 266         msb = inb(TS5500_ADC_CONV_MSB_ADDR);
 267 
 268         return (msb << 8) | lsb;
 269 }
 270 
 271 static struct max197_platform_data ts5500_adc_pdata = {
 272         .convert = ts5500_adc_convert,
 273 };
 274 
 275 static struct platform_device ts5500_adc_pdev = {
 276         .name = "max197",
 277         .id = -1,
 278         .dev = {
 279                 .platform_data = &ts5500_adc_pdata,
 280         },
 281 };
 282 
 283 static int __init ts5500_init(void)
 284 {
 285         struct platform_device *pdev;
 286         struct ts5500_sbc *sbc;
 287         int err;
 288 
 289         /*
 290          * There is no DMI available or PCI bridge subvendor info,
 291          * only the BIOS provides a 16-bit identification call.
 292          * It is safer to find a signature in the BIOS shadow RAM.
 293          */
 294         err = ts5500_check_signature();
 295         if (err)
 296                 return err;
 297 
 298         pdev = platform_device_register_simple("ts5500", -1, NULL, 0);
 299         if (IS_ERR(pdev))
 300                 return PTR_ERR(pdev);
 301 
 302         sbc = devm_kzalloc(&pdev->dev, sizeof(struct ts5500_sbc), GFP_KERNEL);
 303         if (!sbc) {
 304                 err = -ENOMEM;
 305                 goto error;
 306         }
 307 
 308         err = ts5500_detect_config(sbc);
 309         if (err)
 310                 goto error;
 311 
 312         platform_set_drvdata(pdev, sbc);
 313 
 314         err = sysfs_create_group(&pdev->dev.kobj, &ts5500_attr_group);
 315         if (err)
 316                 goto error;
 317 
 318         if (sbc->id == TS5500_PRODUCT_CODE) {
 319                 ts5500_dio1_pdev.dev.parent = &pdev->dev;
 320                 if (platform_device_register(&ts5500_dio1_pdev))
 321                         dev_warn(&pdev->dev, "DIO1 block registration failed\n");
 322                 ts5500_dio2_pdev.dev.parent = &pdev->dev;
 323                 if (platform_device_register(&ts5500_dio2_pdev))
 324                         dev_warn(&pdev->dev, "DIO2 block registration failed\n");
 325         }
 326 
 327         if (led_classdev_register(&pdev->dev, &ts5500_led_cdev))
 328                 dev_warn(&pdev->dev, "LED registration failed\n");
 329 
 330         if (sbc->adc) {
 331                 ts5500_adc_pdev.dev.parent = &pdev->dev;
 332                 if (platform_device_register(&ts5500_adc_pdev))
 333                         dev_warn(&pdev->dev, "ADC registration failed\n");
 334         }
 335 
 336         return 0;
 337 error:
 338         platform_device_unregister(pdev);
 339         return err;
 340 }
 341 device_initcall(ts5500_init);

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