cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ts5500.c (9041B)


      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 */
     73struct 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 */
     85static const struct {
     86	const char * const string;
     87	const ssize_t offset;
     88} ts5500_signatures[] __initconst = {
     89	{ "TS-5x00 AMD Elan", 0xb14 },
     90};
     91
     92static 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
    114static 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
    145cleanup:
    146	release_region(TS5500_PRODUCT_CODE_ADDR, 4);
    147	return ret;
    148}
    149
    150static 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}
    157static DEVICE_ATTR_RO(name);
    158
    159static 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}
    166static DEVICE_ATTR_RO(id);
    167
    168static 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}
    175static 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
    187TS5500_ATTR_BOOL(sram);
    188TS5500_ATTR_BOOL(rs485);
    189TS5500_ATTR_BOOL(adc);
    190TS5500_ATTR_BOOL(ereset);
    191TS5500_ATTR_BOOL(itr);
    192
    193static 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
    205static const struct attribute_group ts5500_attr_group = {
    206	.attrs = ts5500_attributes,
    207};
    208
    209static struct resource ts5500_dio1_resource[] = {
    210	DEFINE_RES_IRQ_NAMED(7, "DIO1 interrupt"),
    211};
    212
    213static struct platform_device ts5500_dio1_pdev = {
    214	.name = "ts5500-dio1",
    215	.id = -1,
    216	.resource = ts5500_dio1_resource,
    217	.num_resources = 1,
    218};
    219
    220static struct resource ts5500_dio2_resource[] = {
    221	DEFINE_RES_IRQ_NAMED(6, "DIO2 interrupt"),
    222};
    223
    224static struct platform_device ts5500_dio2_pdev = {
    225	.name = "ts5500-dio2",
    226	.id = -1,
    227	.resource = ts5500_dio2_resource,
    228	.num_resources = 1,
    229};
    230
    231static void ts5500_led_set(struct led_classdev *led_cdev,
    232			   enum led_brightness brightness)
    233{
    234	outb(!!brightness, TS5500_LED_JP_ADDR);
    235}
    236
    237static 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
    242static struct led_classdev ts5500_led_cdev = {
    243	.name = "ts5500:green:",
    244	.brightness_set = ts5500_led_set,
    245	.brightness_get = ts5500_led_get,
    246};
    247
    248static 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
    271static struct max197_platform_data ts5500_adc_pdata = {
    272	.convert = ts5500_adc_convert,
    273};
    274
    275static struct platform_device ts5500_adc_pdev = {
    276	.name = "max197",
    277	.id = -1,
    278	.dev = {
    279		.platform_data = &ts5500_adc_pdata,
    280	},
    281};
    282
    283static 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;
    337error:
    338	platform_device_unregister(pdev);
    339	return err;
    340}
    341device_initcall(ts5500_init);