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

olpc.c (7946B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Support for the OLPC DCON and OLPC EC access
      4 *
      5 * Copyright © 2006  Advanced Micro Devices, Inc.
      6 * Copyright © 2007-2008  Andres Salomon <dilinger@debian.org>
      7 */
      8
      9#include <linux/kernel.h>
     10#include <linux/init.h>
     11#include <linux/export.h>
     12#include <linux/delay.h>
     13#include <linux/io.h>
     14#include <linux/string.h>
     15#include <linux/platform_device.h>
     16#include <linux/of.h>
     17#include <linux/syscore_ops.h>
     18#include <linux/mutex.h>
     19#include <linux/olpc-ec.h>
     20
     21#include <asm/geode.h>
     22#include <asm/setup.h>
     23#include <asm/olpc.h>
     24#include <asm/olpc_ofw.h>
     25
     26struct olpc_platform_t olpc_platform_info;
     27EXPORT_SYMBOL_GPL(olpc_platform_info);
     28
     29/* what the timeout *should* be (in ms) */
     30#define EC_BASE_TIMEOUT 20
     31
     32/* the timeout that bugs in the EC might force us to actually use */
     33static int ec_timeout = EC_BASE_TIMEOUT;
     34
     35static int __init olpc_ec_timeout_set(char *str)
     36{
     37	if (get_option(&str, &ec_timeout) != 1) {
     38		ec_timeout = EC_BASE_TIMEOUT;
     39		printk(KERN_ERR "olpc-ec:  invalid argument to "
     40				"'olpc_ec_timeout=', ignoring!\n");
     41	}
     42	printk(KERN_DEBUG "olpc-ec:  using %d ms delay for EC commands.\n",
     43			ec_timeout);
     44	return 1;
     45}
     46__setup("olpc_ec_timeout=", olpc_ec_timeout_set);
     47
     48/*
     49 * These {i,o}bf_status functions return whether the buffers are full or not.
     50 */
     51
     52static inline unsigned int ibf_status(unsigned int port)
     53{
     54	return !!(inb(port) & 0x02);
     55}
     56
     57static inline unsigned int obf_status(unsigned int port)
     58{
     59	return inb(port) & 0x01;
     60}
     61
     62#define wait_on_ibf(p, d) __wait_on_ibf(__LINE__, (p), (d))
     63static int __wait_on_ibf(unsigned int line, unsigned int port, int desired)
     64{
     65	unsigned int timeo;
     66	int state = ibf_status(port);
     67
     68	for (timeo = ec_timeout; state != desired && timeo; timeo--) {
     69		mdelay(1);
     70		state = ibf_status(port);
     71	}
     72
     73	if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) &&
     74			timeo < (ec_timeout - EC_BASE_TIMEOUT)) {
     75		printk(KERN_WARNING "olpc-ec:  %d: waited %u ms for IBF!\n",
     76				line, ec_timeout - timeo);
     77	}
     78
     79	return !(state == desired);
     80}
     81
     82#define wait_on_obf(p, d) __wait_on_obf(__LINE__, (p), (d))
     83static int __wait_on_obf(unsigned int line, unsigned int port, int desired)
     84{
     85	unsigned int timeo;
     86	int state = obf_status(port);
     87
     88	for (timeo = ec_timeout; state != desired && timeo; timeo--) {
     89		mdelay(1);
     90		state = obf_status(port);
     91	}
     92
     93	if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) &&
     94			timeo < (ec_timeout - EC_BASE_TIMEOUT)) {
     95		printk(KERN_WARNING "olpc-ec:  %d: waited %u ms for OBF!\n",
     96				line, ec_timeout - timeo);
     97	}
     98
     99	return !(state == desired);
    100}
    101
    102/*
    103 * This allows the kernel to run Embedded Controller commands.  The EC is
    104 * documented at <http://wiki.laptop.org/go/Embedded_controller>, and the
    105 * available EC commands are here:
    106 * <http://wiki.laptop.org/go/Ec_specification>.  Unfortunately, while
    107 * OpenFirmware's source is available, the EC's is not.
    108 */
    109static int olpc_xo1_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf,
    110		size_t outlen, void *arg)
    111{
    112	int ret = -EIO;
    113	int i;
    114	int restarts = 0;
    115
    116	/* Clear OBF */
    117	for (i = 0; i < 10 && (obf_status(0x6c) == 1); i++)
    118		inb(0x68);
    119	if (i == 10) {
    120		printk(KERN_ERR "olpc-ec:  timeout while attempting to "
    121				"clear OBF flag!\n");
    122		goto err;
    123	}
    124
    125	if (wait_on_ibf(0x6c, 0)) {
    126		printk(KERN_ERR "olpc-ec:  timeout waiting for EC to "
    127				"quiesce!\n");
    128		goto err;
    129	}
    130
    131restart:
    132	/*
    133	 * Note that if we time out during any IBF checks, that's a failure;
    134	 * we have to return.  There's no way for the kernel to clear that.
    135	 *
    136	 * If we time out during an OBF check, we can restart the command;
    137	 * reissuing it will clear the OBF flag, and we should be alright.
    138	 * The OBF flag will sometimes misbehave due to what we believe
    139	 * is a hardware quirk..
    140	 */
    141	pr_devel("olpc-ec:  running cmd 0x%x\n", cmd);
    142	outb(cmd, 0x6c);
    143
    144	if (wait_on_ibf(0x6c, 0)) {
    145		printk(KERN_ERR "olpc-ec:  timeout waiting for EC to read "
    146				"command!\n");
    147		goto err;
    148	}
    149
    150	if (inbuf && inlen) {
    151		/* write data to EC */
    152		for (i = 0; i < inlen; i++) {
    153			pr_devel("olpc-ec:  sending cmd arg 0x%x\n", inbuf[i]);
    154			outb(inbuf[i], 0x68);
    155			if (wait_on_ibf(0x6c, 0)) {
    156				printk(KERN_ERR "olpc-ec:  timeout waiting for"
    157						" EC accept data!\n");
    158				goto err;
    159			}
    160		}
    161	}
    162	if (outbuf && outlen) {
    163		/* read data from EC */
    164		for (i = 0; i < outlen; i++) {
    165			if (wait_on_obf(0x6c, 1)) {
    166				printk(KERN_ERR "olpc-ec:  timeout waiting for"
    167						" EC to provide data!\n");
    168				if (restarts++ < 10)
    169					goto restart;
    170				goto err;
    171			}
    172			outbuf[i] = inb(0x68);
    173			pr_devel("olpc-ec:  received 0x%x\n", outbuf[i]);
    174		}
    175	}
    176
    177	ret = 0;
    178err:
    179	return ret;
    180}
    181
    182static bool __init check_ofw_architecture(struct device_node *root)
    183{
    184	const char *olpc_arch;
    185	int propsize;
    186
    187	olpc_arch = of_get_property(root, "architecture", &propsize);
    188	return propsize == 5 && strncmp("OLPC", olpc_arch, 5) == 0;
    189}
    190
    191static u32 __init get_board_revision(struct device_node *root)
    192{
    193	int propsize;
    194	const __be32 *rev;
    195
    196	rev = of_get_property(root, "board-revision-int", &propsize);
    197	if (propsize != 4)
    198		return 0;
    199
    200	return be32_to_cpu(*rev);
    201}
    202
    203static bool __init platform_detect(void)
    204{
    205	struct device_node *root = of_find_node_by_path("/");
    206	bool success;
    207
    208	if (!root)
    209		return false;
    210
    211	success = check_ofw_architecture(root);
    212	if (success) {
    213		olpc_platform_info.boardrev = get_board_revision(root);
    214		olpc_platform_info.flags |= OLPC_F_PRESENT;
    215
    216		pr_info("OLPC board revision %s%X\n",
    217			((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
    218			olpc_platform_info.boardrev >> 4);
    219	}
    220
    221	of_node_put(root);
    222	return success;
    223}
    224
    225static int __init add_xo1_platform_devices(void)
    226{
    227	struct platform_device *pdev;
    228
    229	pdev = platform_device_register_simple("xo1-rfkill", -1, NULL, 0);
    230	if (IS_ERR(pdev))
    231		return PTR_ERR(pdev);
    232
    233	pdev = platform_device_register_simple("olpc-xo1", -1, NULL, 0);
    234
    235	return PTR_ERR_OR_ZERO(pdev);
    236}
    237
    238static int olpc_xo1_ec_suspend(struct platform_device *pdev)
    239{
    240	/*
    241	 * Squelch SCIs while suspended.  This is a fix for
    242	 * <http://dev.laptop.org/ticket/1835>.
    243	 */
    244	return olpc_ec_cmd(EC_SET_SCI_INHIBIT, NULL, 0, NULL, 0);
    245}
    246
    247static int olpc_xo1_ec_resume(struct platform_device *pdev)
    248{
    249	/* Tell the EC to stop inhibiting SCIs */
    250	olpc_ec_cmd(EC_SET_SCI_INHIBIT_RELEASE, NULL, 0, NULL, 0);
    251
    252	/*
    253	 * Tell the wireless module to restart USB communication.
    254	 * Must be done twice.
    255	 */
    256	olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
    257	olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
    258
    259	return 0;
    260}
    261
    262static struct olpc_ec_driver ec_xo1_driver = {
    263	.suspend = olpc_xo1_ec_suspend,
    264	.resume = olpc_xo1_ec_resume,
    265	.ec_cmd = olpc_xo1_ec_cmd,
    266#ifdef CONFIG_OLPC_XO1_SCI
    267	/*
    268	 * XO-1 EC wakeups are available when olpc-xo1-sci driver is
    269	 * compiled in
    270	 */
    271	.wakeup_available = true,
    272#endif
    273};
    274
    275static struct olpc_ec_driver ec_xo1_5_driver = {
    276	.ec_cmd = olpc_xo1_ec_cmd,
    277#ifdef CONFIG_OLPC_XO15_SCI
    278	/*
    279	 * XO-1.5 EC wakeups are available when olpc-xo15-sci driver is
    280	 * compiled in
    281	 */
    282	.wakeup_available = true,
    283#endif
    284};
    285
    286static int __init olpc_init(void)
    287{
    288	int r = 0;
    289
    290	if (!olpc_ofw_present() || !platform_detect())
    291		return 0;
    292
    293	/* register the XO-1 and 1.5-specific EC handler */
    294	if (olpc_platform_info.boardrev < olpc_board_pre(0xd0))	/* XO-1 */
    295		olpc_ec_driver_register(&ec_xo1_driver, NULL);
    296	else
    297		olpc_ec_driver_register(&ec_xo1_5_driver, NULL);
    298	platform_device_register_simple("olpc-ec", -1, NULL, 0);
    299
    300	/* assume B1 and above models always have a DCON */
    301	if (olpc_board_at_least(olpc_board(0xb1)))
    302		olpc_platform_info.flags |= OLPC_F_DCON;
    303
    304#ifdef CONFIG_PCI_OLPC
    305	/* If the VSA exists let it emulate PCI, if not emulate in kernel.
    306	 * XO-1 only. */
    307	if (olpc_platform_info.boardrev < olpc_board_pre(0xd0) &&
    308			!cs5535_has_vsa2())
    309		x86_init.pci.arch_init = pci_olpc_init;
    310#endif
    311
    312	if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) { /* XO-1 */
    313		r = add_xo1_platform_devices();
    314		if (r)
    315			return r;
    316	}
    317
    318	return 0;
    319}
    320
    321postcore_initcall(olpc_init);