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

tqmx86.c (7850B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * TQ-Systems PLD MFD core driver, based on vendor driver by
      4 * Vadim V.Vlasov <vvlasov@dev.rtsoft.ru>
      5 *
      6 * Copyright (c) 2015 TQ-Systems GmbH
      7 * Copyright (c) 2019 Andrew Lunn <andrew@lunn.ch>
      8 */
      9
     10#include <linux/delay.h>
     11#include <linux/dmi.h>
     12#include <linux/i2c.h>
     13#include <linux/io.h>
     14#include <linux/mfd/core.h>
     15#include <linux/module.h>
     16#include <linux/platform_data/i2c-ocores.h>
     17#include <linux/platform_device.h>
     18
     19#define TQMX86_IOBASE	0x160
     20#define TQMX86_IOSIZE	0x3f
     21#define TQMX86_IOBASE_I2C	0x1a0
     22#define TQMX86_IOSIZE_I2C	0xa
     23#define TQMX86_IOBASE_WATCHDOG	0x18b
     24#define TQMX86_IOSIZE_WATCHDOG	0x2
     25#define TQMX86_IOBASE_GPIO	0x18d
     26#define TQMX86_IOSIZE_GPIO	0x4
     27
     28#define TQMX86_REG_BOARD_ID	0x20
     29#define TQMX86_REG_BOARD_ID_E38M	1
     30#define TQMX86_REG_BOARD_ID_50UC	2
     31#define TQMX86_REG_BOARD_ID_E38C	3
     32#define TQMX86_REG_BOARD_ID_60EB	4
     33#define TQMX86_REG_BOARD_ID_E39M	5
     34#define TQMX86_REG_BOARD_ID_E39C	6
     35#define TQMX86_REG_BOARD_ID_E39x	7
     36#define TQMX86_REG_BOARD_ID_70EB	8
     37#define TQMX86_REG_BOARD_ID_80UC	9
     38#define TQMX86_REG_BOARD_ID_110EB	11
     39#define TQMX86_REG_BOARD_ID_E40M	12
     40#define TQMX86_REG_BOARD_ID_E40S	13
     41#define TQMX86_REG_BOARD_ID_E40C1	14
     42#define TQMX86_REG_BOARD_ID_E40C2	15
     43#define TQMX86_REG_BOARD_REV	0x21
     44#define TQMX86_REG_IO_EXT_INT	0x26
     45#define TQMX86_REG_IO_EXT_INT_NONE		0
     46#define TQMX86_REG_IO_EXT_INT_7			1
     47#define TQMX86_REG_IO_EXT_INT_9			2
     48#define TQMX86_REG_IO_EXT_INT_12		3
     49#define TQMX86_REG_IO_EXT_INT_MASK		0x3
     50#define TQMX86_REG_IO_EXT_INT_GPIO_SHIFT	4
     51
     52#define TQMX86_REG_I2C_DETECT	0x47
     53#define TQMX86_REG_I2C_DETECT_SOFT		0xa5
     54#define TQMX86_REG_I2C_INT_EN	0x49
     55
     56static uint gpio_irq;
     57module_param(gpio_irq, uint, 0);
     58MODULE_PARM_DESC(gpio_irq, "GPIO IRQ number (7, 9, 12)");
     59
     60static const struct resource tqmx_i2c_soft_resources[] = {
     61	DEFINE_RES_IO(TQMX86_IOBASE_I2C, TQMX86_IOSIZE_I2C),
     62};
     63
     64static const struct resource tqmx_watchdog_resources[] = {
     65	DEFINE_RES_IO(TQMX86_IOBASE_WATCHDOG, TQMX86_IOSIZE_WATCHDOG),
     66};
     67
     68/*
     69 * The IRQ resource must be first, since it is updated with the
     70 * configured IRQ in the probe function.
     71 */
     72static struct resource tqmx_gpio_resources[] = {
     73	DEFINE_RES_IRQ(0),
     74	DEFINE_RES_IO(TQMX86_IOBASE_GPIO, TQMX86_IOSIZE_GPIO),
     75};
     76
     77static struct i2c_board_info tqmx86_i2c_devices[] = {
     78	{
     79		/* 4K EEPROM at 0x50 */
     80		I2C_BOARD_INFO("24c32", 0x50),
     81	},
     82};
     83
     84static struct ocores_i2c_platform_data ocores_platform_data = {
     85	.num_devices = ARRAY_SIZE(tqmx86_i2c_devices),
     86	.devices = tqmx86_i2c_devices,
     87};
     88
     89static const struct mfd_cell tqmx86_i2c_soft_dev[] = {
     90	{
     91		.name = "ocores-i2c",
     92		.platform_data = &ocores_platform_data,
     93		.pdata_size = sizeof(ocores_platform_data),
     94		.resources = tqmx_i2c_soft_resources,
     95		.num_resources = ARRAY_SIZE(tqmx_i2c_soft_resources),
     96	},
     97};
     98
     99static const struct mfd_cell tqmx86_devs[] = {
    100	{
    101		.name = "tqmx86-wdt",
    102		.resources = tqmx_watchdog_resources,
    103		.num_resources = ARRAY_SIZE(tqmx_watchdog_resources),
    104		.ignore_resource_conflicts = true,
    105	},
    106	{
    107		.name = "tqmx86-gpio",
    108		.resources = tqmx_gpio_resources,
    109		.num_resources = ARRAY_SIZE(tqmx_gpio_resources),
    110		.ignore_resource_conflicts = true,
    111	},
    112};
    113
    114static const char *tqmx86_board_id_to_name(u8 board_id)
    115{
    116	switch (board_id) {
    117	case TQMX86_REG_BOARD_ID_E38M:
    118		return "TQMxE38M";
    119	case TQMX86_REG_BOARD_ID_50UC:
    120		return "TQMx50UC";
    121	case TQMX86_REG_BOARD_ID_E38C:
    122		return "TQMxE38C";
    123	case TQMX86_REG_BOARD_ID_60EB:
    124		return "TQMx60EB";
    125	case TQMX86_REG_BOARD_ID_E39M:
    126		return "TQMxE39M";
    127	case TQMX86_REG_BOARD_ID_E39C:
    128		return "TQMxE39C";
    129	case TQMX86_REG_BOARD_ID_E39x:
    130		return "TQMxE39x";
    131	case TQMX86_REG_BOARD_ID_70EB:
    132		return "TQMx70EB";
    133	case TQMX86_REG_BOARD_ID_80UC:
    134		return "TQMx80UC";
    135	case TQMX86_REG_BOARD_ID_110EB:
    136		return "TQMx110EB";
    137	case TQMX86_REG_BOARD_ID_E40M:
    138		return "TQMxE40M";
    139	case TQMX86_REG_BOARD_ID_E40S:
    140		return "TQMxE40S";
    141	case TQMX86_REG_BOARD_ID_E40C1:
    142		return "TQMxE40C1";
    143	case TQMX86_REG_BOARD_ID_E40C2:
    144		return "TQMxE40C2";
    145	default:
    146		return "Unknown";
    147	}
    148}
    149
    150static int tqmx86_board_id_to_clk_rate(struct device *dev, u8 board_id)
    151{
    152	switch (board_id) {
    153	case TQMX86_REG_BOARD_ID_50UC:
    154	case TQMX86_REG_BOARD_ID_60EB:
    155	case TQMX86_REG_BOARD_ID_70EB:
    156	case TQMX86_REG_BOARD_ID_80UC:
    157	case TQMX86_REG_BOARD_ID_110EB:
    158	case TQMX86_REG_BOARD_ID_E40M:
    159	case TQMX86_REG_BOARD_ID_E40S:
    160	case TQMX86_REG_BOARD_ID_E40C1:
    161	case TQMX86_REG_BOARD_ID_E40C2:
    162		return 24000;
    163	case TQMX86_REG_BOARD_ID_E39M:
    164	case TQMX86_REG_BOARD_ID_E39C:
    165	case TQMX86_REG_BOARD_ID_E39x:
    166		return 25000;
    167	case TQMX86_REG_BOARD_ID_E38M:
    168	case TQMX86_REG_BOARD_ID_E38C:
    169		return 33000;
    170	default:
    171		dev_warn(dev, "unknown board %d, assuming 24MHz LPC clock\n",
    172			 board_id);
    173		return 24000;
    174	}
    175}
    176
    177static int tqmx86_probe(struct platform_device *pdev)
    178{
    179	u8 board_id, rev, i2c_det, io_ext_int_val;
    180	struct device *dev = &pdev->dev;
    181	u8 gpio_irq_cfg, readback;
    182	const char *board_name;
    183	void __iomem *io_base;
    184	int err;
    185
    186	switch (gpio_irq) {
    187	case 0:
    188		gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_NONE;
    189		break;
    190	case 7:
    191		gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_7;
    192		break;
    193	case 9:
    194		gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_9;
    195		break;
    196	case 12:
    197		gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_12;
    198		break;
    199	default:
    200		pr_err("tqmx86: Invalid GPIO IRQ (%d)\n", gpio_irq);
    201		return -EINVAL;
    202	}
    203
    204	io_base = devm_ioport_map(dev, TQMX86_IOBASE, TQMX86_IOSIZE);
    205	if (!io_base)
    206		return -ENOMEM;
    207
    208	board_id = ioread8(io_base + TQMX86_REG_BOARD_ID);
    209	board_name = tqmx86_board_id_to_name(board_id);
    210	rev = ioread8(io_base + TQMX86_REG_BOARD_REV);
    211
    212	dev_info(dev,
    213		 "Found %s - Board ID %d, PCB Revision %d, PLD Revision %d\n",
    214		 board_name, board_id, rev >> 4, rev & 0xf);
    215
    216	i2c_det = ioread8(io_base + TQMX86_REG_I2C_DETECT);
    217
    218	if (gpio_irq_cfg) {
    219		io_ext_int_val =
    220			gpio_irq_cfg << TQMX86_REG_IO_EXT_INT_GPIO_SHIFT;
    221		iowrite8(io_ext_int_val, io_base + TQMX86_REG_IO_EXT_INT);
    222		readback = ioread8(io_base + TQMX86_REG_IO_EXT_INT);
    223		if (readback != io_ext_int_val) {
    224			dev_warn(dev, "GPIO interrupts not supported.\n");
    225			return -EINVAL;
    226		}
    227
    228		/* Assumes the IRQ resource is first. */
    229		tqmx_gpio_resources[0].start = gpio_irq;
    230	} else {
    231		tqmx_gpio_resources[0].flags = 0;
    232	}
    233
    234	ocores_platform_data.clock_khz = tqmx86_board_id_to_clk_rate(dev, board_id);
    235
    236	if (i2c_det == TQMX86_REG_I2C_DETECT_SOFT) {
    237		err = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
    238					   tqmx86_i2c_soft_dev,
    239					   ARRAY_SIZE(tqmx86_i2c_soft_dev),
    240					   NULL, 0, NULL);
    241		if (err)
    242			return err;
    243	}
    244
    245	return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
    246				    tqmx86_devs,
    247				    ARRAY_SIZE(tqmx86_devs),
    248				    NULL, 0, NULL);
    249}
    250
    251static int tqmx86_create_platform_device(const struct dmi_system_id *id)
    252{
    253	struct platform_device *pdev;
    254	int err;
    255
    256	pdev = platform_device_alloc("tqmx86", -1);
    257	if (!pdev)
    258		return -ENOMEM;
    259
    260	err = platform_device_add(pdev);
    261	if (err)
    262		platform_device_put(pdev);
    263
    264	return err;
    265}
    266
    267static const struct dmi_system_id tqmx86_dmi_table[] __initconst = {
    268	{
    269		.ident = "TQMX86",
    270		.matches = {
    271			DMI_MATCH(DMI_SYS_VENDOR, "TQ-Group"),
    272			DMI_MATCH(DMI_PRODUCT_NAME, "TQMx"),
    273		},
    274		.callback = tqmx86_create_platform_device,
    275	},
    276	{
    277		.ident = "TQMX86",
    278		.matches = {
    279			DMI_MATCH(DMI_SYS_VENDOR, "TQ-Systems"),
    280			DMI_MATCH(DMI_PRODUCT_NAME, "TQMx"),
    281		},
    282		.callback = tqmx86_create_platform_device,
    283	},
    284	{}
    285};
    286MODULE_DEVICE_TABLE(dmi, tqmx86_dmi_table);
    287
    288static struct platform_driver tqmx86_driver = {
    289	.driver		= {
    290		.name	= "tqmx86",
    291	},
    292	.probe		= tqmx86_probe,
    293};
    294
    295static int __init tqmx86_init(void)
    296{
    297	if (!dmi_check_system(tqmx86_dmi_table))
    298		return -ENODEV;
    299
    300	return platform_driver_register(&tqmx86_driver);
    301}
    302
    303module_init(tqmx86_init);
    304
    305MODULE_DESCRIPTION("TQMx86 PLD Core Driver");
    306MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
    307MODULE_LICENSE("GPL");
    308MODULE_ALIAS("platform:tqmx86");