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

iosf_mbi.c (15198B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * IOSF-SB MailBox Interface Driver
      4 * Copyright (c) 2013, Intel Corporation.
      5 *
      6 * The IOSF-SB is a fabric bus available on Atom based SOC's that uses a
      7 * mailbox interface (MBI) to communicate with multiple devices. This
      8 * driver implements access to this interface for those platforms that can
      9 * enumerate the device using PCI.
     10 */
     11
     12#include <linux/delay.h>
     13#include <linux/module.h>
     14#include <linux/init.h>
     15#include <linux/spinlock.h>
     16#include <linux/pci.h>
     17#include <linux/debugfs.h>
     18#include <linux/capability.h>
     19#include <linux/pm_qos.h>
     20#include <linux/wait.h>
     21
     22#include <asm/iosf_mbi.h>
     23
     24#define PCI_DEVICE_ID_INTEL_BAYTRAIL		0x0F00
     25#define PCI_DEVICE_ID_INTEL_BRASWELL		0x2280
     26#define PCI_DEVICE_ID_INTEL_QUARK_X1000		0x0958
     27#define PCI_DEVICE_ID_INTEL_TANGIER		0x1170
     28
     29static struct pci_dev *mbi_pdev;
     30static DEFINE_SPINLOCK(iosf_mbi_lock);
     31
     32/**************** Generic iosf_mbi access helpers ****************/
     33
     34static inline u32 iosf_mbi_form_mcr(u8 op, u8 port, u8 offset)
     35{
     36	return (op << 24) | (port << 16) | (offset << 8) | MBI_ENABLE;
     37}
     38
     39static int iosf_mbi_pci_read_mdr(u32 mcrx, u32 mcr, u32 *mdr)
     40{
     41	int result;
     42
     43	if (!mbi_pdev)
     44		return -ENODEV;
     45
     46	if (mcrx) {
     47		result = pci_write_config_dword(mbi_pdev, MBI_MCRX_OFFSET,
     48						mcrx);
     49		if (result < 0)
     50			goto fail_read;
     51	}
     52
     53	result = pci_write_config_dword(mbi_pdev, MBI_MCR_OFFSET, mcr);
     54	if (result < 0)
     55		goto fail_read;
     56
     57	result = pci_read_config_dword(mbi_pdev, MBI_MDR_OFFSET, mdr);
     58	if (result < 0)
     59		goto fail_read;
     60
     61	return 0;
     62
     63fail_read:
     64	dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result);
     65	return result;
     66}
     67
     68static int iosf_mbi_pci_write_mdr(u32 mcrx, u32 mcr, u32 mdr)
     69{
     70	int result;
     71
     72	if (!mbi_pdev)
     73		return -ENODEV;
     74
     75	result = pci_write_config_dword(mbi_pdev, MBI_MDR_OFFSET, mdr);
     76	if (result < 0)
     77		goto fail_write;
     78
     79	if (mcrx) {
     80		result = pci_write_config_dword(mbi_pdev, MBI_MCRX_OFFSET,
     81						mcrx);
     82		if (result < 0)
     83			goto fail_write;
     84	}
     85
     86	result = pci_write_config_dword(mbi_pdev, MBI_MCR_OFFSET, mcr);
     87	if (result < 0)
     88		goto fail_write;
     89
     90	return 0;
     91
     92fail_write:
     93	dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result);
     94	return result;
     95}
     96
     97int iosf_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr)
     98{
     99	u32 mcr, mcrx;
    100	unsigned long flags;
    101	int ret;
    102
    103	/* Access to the GFX unit is handled by GPU code */
    104	if (port == BT_MBI_UNIT_GFX) {
    105		WARN_ON(1);
    106		return -EPERM;
    107	}
    108
    109	mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO);
    110	mcrx = offset & MBI_MASK_HI;
    111
    112	spin_lock_irqsave(&iosf_mbi_lock, flags);
    113	ret = iosf_mbi_pci_read_mdr(mcrx, mcr, mdr);
    114	spin_unlock_irqrestore(&iosf_mbi_lock, flags);
    115
    116	return ret;
    117}
    118EXPORT_SYMBOL(iosf_mbi_read);
    119
    120int iosf_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr)
    121{
    122	u32 mcr, mcrx;
    123	unsigned long flags;
    124	int ret;
    125
    126	/* Access to the GFX unit is handled by GPU code */
    127	if (port == BT_MBI_UNIT_GFX) {
    128		WARN_ON(1);
    129		return -EPERM;
    130	}
    131
    132	mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO);
    133	mcrx = offset & MBI_MASK_HI;
    134
    135	spin_lock_irqsave(&iosf_mbi_lock, flags);
    136	ret = iosf_mbi_pci_write_mdr(mcrx, mcr, mdr);
    137	spin_unlock_irqrestore(&iosf_mbi_lock, flags);
    138
    139	return ret;
    140}
    141EXPORT_SYMBOL(iosf_mbi_write);
    142
    143int iosf_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask)
    144{
    145	u32 mcr, mcrx;
    146	u32 value;
    147	unsigned long flags;
    148	int ret;
    149
    150	/* Access to the GFX unit is handled by GPU code */
    151	if (port == BT_MBI_UNIT_GFX) {
    152		WARN_ON(1);
    153		return -EPERM;
    154	}
    155
    156	mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO);
    157	mcrx = offset & MBI_MASK_HI;
    158
    159	spin_lock_irqsave(&iosf_mbi_lock, flags);
    160
    161	/* Read current mdr value */
    162	ret = iosf_mbi_pci_read_mdr(mcrx, mcr & MBI_RD_MASK, &value);
    163	if (ret < 0) {
    164		spin_unlock_irqrestore(&iosf_mbi_lock, flags);
    165		return ret;
    166	}
    167
    168	/* Apply mask */
    169	value &= ~mask;
    170	mdr &= mask;
    171	value |= mdr;
    172
    173	/* Write back */
    174	ret = iosf_mbi_pci_write_mdr(mcrx, mcr | MBI_WR_MASK, value);
    175
    176	spin_unlock_irqrestore(&iosf_mbi_lock, flags);
    177
    178	return ret;
    179}
    180EXPORT_SYMBOL(iosf_mbi_modify);
    181
    182bool iosf_mbi_available(void)
    183{
    184	/* Mbi isn't hot-pluggable. No remove routine is provided */
    185	return mbi_pdev;
    186}
    187EXPORT_SYMBOL(iosf_mbi_available);
    188
    189/*
    190 **************** P-Unit/kernel shared I2C bus arbitration ****************
    191 *
    192 * Some Bay Trail and Cherry Trail devices have the P-Unit and us (the kernel)
    193 * share a single I2C bus to the PMIC. Below are helpers to arbitrate the
    194 * accesses between the kernel and the P-Unit.
    195 *
    196 * See arch/x86/include/asm/iosf_mbi.h for kernel-doc text for each function.
    197 */
    198
    199#define SEMAPHORE_TIMEOUT		500
    200#define PUNIT_SEMAPHORE_BYT		0x7
    201#define PUNIT_SEMAPHORE_CHT		0x10e
    202#define PUNIT_SEMAPHORE_BIT		BIT(0)
    203#define PUNIT_SEMAPHORE_ACQUIRE		BIT(1)
    204
    205static DEFINE_MUTEX(iosf_mbi_pmic_access_mutex);
    206static BLOCKING_NOTIFIER_HEAD(iosf_mbi_pmic_bus_access_notifier);
    207static DECLARE_WAIT_QUEUE_HEAD(iosf_mbi_pmic_access_waitq);
    208static u32 iosf_mbi_pmic_punit_access_count;
    209static u32 iosf_mbi_pmic_i2c_access_count;
    210static u32 iosf_mbi_sem_address;
    211static unsigned long iosf_mbi_sem_acquired;
    212static struct pm_qos_request iosf_mbi_pm_qos;
    213
    214void iosf_mbi_punit_acquire(void)
    215{
    216	/* Wait for any I2C PMIC accesses from in kernel drivers to finish. */
    217	mutex_lock(&iosf_mbi_pmic_access_mutex);
    218	while (iosf_mbi_pmic_i2c_access_count != 0) {
    219		mutex_unlock(&iosf_mbi_pmic_access_mutex);
    220		wait_event(iosf_mbi_pmic_access_waitq,
    221			   iosf_mbi_pmic_i2c_access_count == 0);
    222		mutex_lock(&iosf_mbi_pmic_access_mutex);
    223	}
    224	/*
    225	 * We do not need to do anything to allow the PUNIT to safely access
    226	 * the PMIC, other then block in kernel accesses to the PMIC.
    227	 */
    228	iosf_mbi_pmic_punit_access_count++;
    229	mutex_unlock(&iosf_mbi_pmic_access_mutex);
    230}
    231EXPORT_SYMBOL(iosf_mbi_punit_acquire);
    232
    233void iosf_mbi_punit_release(void)
    234{
    235	bool do_wakeup;
    236
    237	mutex_lock(&iosf_mbi_pmic_access_mutex);
    238	iosf_mbi_pmic_punit_access_count--;
    239	do_wakeup = iosf_mbi_pmic_punit_access_count == 0;
    240	mutex_unlock(&iosf_mbi_pmic_access_mutex);
    241
    242	if (do_wakeup)
    243		wake_up(&iosf_mbi_pmic_access_waitq);
    244}
    245EXPORT_SYMBOL(iosf_mbi_punit_release);
    246
    247static int iosf_mbi_get_sem(u32 *sem)
    248{
    249	int ret;
    250
    251	ret = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
    252			    iosf_mbi_sem_address, sem);
    253	if (ret) {
    254		dev_err(&mbi_pdev->dev, "Error P-Unit semaphore read failed\n");
    255		return ret;
    256	}
    257
    258	*sem &= PUNIT_SEMAPHORE_BIT;
    259	return 0;
    260}
    261
    262static void iosf_mbi_reset_semaphore(void)
    263{
    264	if (iosf_mbi_modify(BT_MBI_UNIT_PMC, MBI_REG_READ,
    265			    iosf_mbi_sem_address, 0, PUNIT_SEMAPHORE_BIT))
    266		dev_err(&mbi_pdev->dev, "Error P-Unit semaphore reset failed\n");
    267
    268	cpu_latency_qos_update_request(&iosf_mbi_pm_qos, PM_QOS_DEFAULT_VALUE);
    269
    270	blocking_notifier_call_chain(&iosf_mbi_pmic_bus_access_notifier,
    271				     MBI_PMIC_BUS_ACCESS_END, NULL);
    272}
    273
    274/*
    275 * This function blocks P-Unit accesses to the PMIC I2C bus, so that kernel
    276 * I2C code, such as e.g. a fuel-gauge driver, can access it safely.
    277 *
    278 * This function may be called by I2C controller code while an I2C driver has
    279 * already blocked P-Unit accesses because it wants them blocked over multiple
    280 * i2c-transfers, for e.g. read-modify-write of an I2C client register.
    281 *
    282 * To allow safe PMIC i2c bus accesses this function takes the following steps:
    283 *
    284 * 1) Some code sends request to the P-Unit which make it access the PMIC
    285 *    I2C bus. Testing has shown that the P-Unit does not check its internal
    286 *    PMIC bus semaphore for these requests. Callers of these requests call
    287 *    iosf_mbi_punit_acquire()/_release() around their P-Unit accesses, these
    288 *    functions increase/decrease iosf_mbi_pmic_punit_access_count, so first
    289 *    we wait for iosf_mbi_pmic_punit_access_count to become 0.
    290 *
    291 * 2) Check iosf_mbi_pmic_i2c_access_count, if access has already
    292 *    been blocked by another caller, we only need to increment
    293 *    iosf_mbi_pmic_i2c_access_count and we can skip the other steps.
    294 *
    295 * 3) Some code makes such P-Unit requests from atomic contexts where it
    296 *    cannot call iosf_mbi_punit_acquire() as that may sleep.
    297 *    As the second step we call a notifier chain which allows any code
    298 *    needing P-Unit resources from atomic context to acquire them before
    299 *    we take control over the PMIC I2C bus.
    300 *
    301 * 4) When CPU cores enter C6 or C7 the P-Unit needs to talk to the PMIC
    302 *    if this happens while the kernel itself is accessing the PMIC I2C bus
    303 *    the SoC hangs.
    304 *    As the third step we call cpu_latency_qos_update_request() to disallow the
    305 *    CPU to enter C6 or C7.
    306 *
    307 * 5) The P-Unit has a PMIC bus semaphore which we can request to stop
    308 *    autonomous P-Unit tasks from accessing the PMIC I2C bus while we hold it.
    309 *    As the fourth and final step we request this semaphore and wait for our
    310 *    request to be acknowledged.
    311 */
    312int iosf_mbi_block_punit_i2c_access(void)
    313{
    314	unsigned long start, end;
    315	int ret = 0;
    316	u32 sem;
    317
    318	if (WARN_ON(!mbi_pdev || !iosf_mbi_sem_address))
    319		return -ENXIO;
    320
    321	mutex_lock(&iosf_mbi_pmic_access_mutex);
    322
    323	while (iosf_mbi_pmic_punit_access_count != 0) {
    324		mutex_unlock(&iosf_mbi_pmic_access_mutex);
    325		wait_event(iosf_mbi_pmic_access_waitq,
    326			   iosf_mbi_pmic_punit_access_count == 0);
    327		mutex_lock(&iosf_mbi_pmic_access_mutex);
    328	}
    329
    330	if (iosf_mbi_pmic_i2c_access_count > 0)
    331		goto success;
    332
    333	blocking_notifier_call_chain(&iosf_mbi_pmic_bus_access_notifier,
    334				     MBI_PMIC_BUS_ACCESS_BEGIN, NULL);
    335
    336	/*
    337	 * Disallow the CPU to enter C6 or C7 state, entering these states
    338	 * requires the P-Unit to talk to the PMIC and if this happens while
    339	 * we're holding the semaphore, the SoC hangs.
    340	 */
    341	cpu_latency_qos_update_request(&iosf_mbi_pm_qos, 0);
    342
    343	/* host driver writes to side band semaphore register */
    344	ret = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
    345			     iosf_mbi_sem_address, PUNIT_SEMAPHORE_ACQUIRE);
    346	if (ret) {
    347		dev_err(&mbi_pdev->dev, "Error P-Unit semaphore request failed\n");
    348		goto error;
    349	}
    350
    351	/* host driver waits for bit 0 to be set in semaphore register */
    352	start = jiffies;
    353	end = start + msecs_to_jiffies(SEMAPHORE_TIMEOUT);
    354	do {
    355		ret = iosf_mbi_get_sem(&sem);
    356		if (!ret && sem) {
    357			iosf_mbi_sem_acquired = jiffies;
    358			dev_dbg(&mbi_pdev->dev, "P-Unit semaphore acquired after %ums\n",
    359				jiffies_to_msecs(jiffies - start));
    360			goto success;
    361		}
    362
    363		usleep_range(1000, 2000);
    364	} while (time_before(jiffies, end));
    365
    366	ret = -ETIMEDOUT;
    367	dev_err(&mbi_pdev->dev, "Error P-Unit semaphore timed out, resetting\n");
    368error:
    369	iosf_mbi_reset_semaphore();
    370	if (!iosf_mbi_get_sem(&sem))
    371		dev_err(&mbi_pdev->dev, "P-Unit semaphore: %d\n", sem);
    372success:
    373	if (!WARN_ON(ret))
    374		iosf_mbi_pmic_i2c_access_count++;
    375
    376	mutex_unlock(&iosf_mbi_pmic_access_mutex);
    377
    378	return ret;
    379}
    380EXPORT_SYMBOL(iosf_mbi_block_punit_i2c_access);
    381
    382void iosf_mbi_unblock_punit_i2c_access(void)
    383{
    384	bool do_wakeup = false;
    385
    386	mutex_lock(&iosf_mbi_pmic_access_mutex);
    387	iosf_mbi_pmic_i2c_access_count--;
    388	if (iosf_mbi_pmic_i2c_access_count == 0) {
    389		iosf_mbi_reset_semaphore();
    390		dev_dbg(&mbi_pdev->dev, "punit semaphore held for %ums\n",
    391			jiffies_to_msecs(jiffies - iosf_mbi_sem_acquired));
    392		do_wakeup = true;
    393	}
    394	mutex_unlock(&iosf_mbi_pmic_access_mutex);
    395
    396	if (do_wakeup)
    397		wake_up(&iosf_mbi_pmic_access_waitq);
    398}
    399EXPORT_SYMBOL(iosf_mbi_unblock_punit_i2c_access);
    400
    401int iosf_mbi_register_pmic_bus_access_notifier(struct notifier_block *nb)
    402{
    403	int ret;
    404
    405	/* Wait for the bus to go inactive before registering */
    406	iosf_mbi_punit_acquire();
    407	ret = blocking_notifier_chain_register(
    408				&iosf_mbi_pmic_bus_access_notifier, nb);
    409	iosf_mbi_punit_release();
    410
    411	return ret;
    412}
    413EXPORT_SYMBOL(iosf_mbi_register_pmic_bus_access_notifier);
    414
    415int iosf_mbi_unregister_pmic_bus_access_notifier_unlocked(
    416	struct notifier_block *nb)
    417{
    418	iosf_mbi_assert_punit_acquired();
    419
    420	return blocking_notifier_chain_unregister(
    421				&iosf_mbi_pmic_bus_access_notifier, nb);
    422}
    423EXPORT_SYMBOL(iosf_mbi_unregister_pmic_bus_access_notifier_unlocked);
    424
    425int iosf_mbi_unregister_pmic_bus_access_notifier(struct notifier_block *nb)
    426{
    427	int ret;
    428
    429	/* Wait for the bus to go inactive before unregistering */
    430	iosf_mbi_punit_acquire();
    431	ret = iosf_mbi_unregister_pmic_bus_access_notifier_unlocked(nb);
    432	iosf_mbi_punit_release();
    433
    434	return ret;
    435}
    436EXPORT_SYMBOL(iosf_mbi_unregister_pmic_bus_access_notifier);
    437
    438void iosf_mbi_assert_punit_acquired(void)
    439{
    440	WARN_ON(iosf_mbi_pmic_punit_access_count == 0);
    441}
    442EXPORT_SYMBOL(iosf_mbi_assert_punit_acquired);
    443
    444/**************** iosf_mbi debug code ****************/
    445
    446#ifdef CONFIG_IOSF_MBI_DEBUG
    447static u32	dbg_mdr;
    448static u32	dbg_mcr;
    449static u32	dbg_mcrx;
    450
    451static int mcr_get(void *data, u64 *val)
    452{
    453	*val = *(u32 *)data;
    454	return 0;
    455}
    456
    457static int mcr_set(void *data, u64 val)
    458{
    459	u8 command = ((u32)val & 0xFF000000) >> 24,
    460	   port	   = ((u32)val & 0x00FF0000) >> 16,
    461	   offset  = ((u32)val & 0x0000FF00) >> 8;
    462	int err;
    463
    464	*(u32 *)data = val;
    465
    466	if (!capable(CAP_SYS_RAWIO))
    467		return -EACCES;
    468
    469	if (command & 1u)
    470		err = iosf_mbi_write(port,
    471			       command,
    472			       dbg_mcrx | offset,
    473			       dbg_mdr);
    474	else
    475		err = iosf_mbi_read(port,
    476			      command,
    477			      dbg_mcrx | offset,
    478			      &dbg_mdr);
    479
    480	return err;
    481}
    482DEFINE_SIMPLE_ATTRIBUTE(iosf_mcr_fops, mcr_get, mcr_set , "%llx\n");
    483
    484static struct dentry *iosf_dbg;
    485
    486static void iosf_sideband_debug_init(void)
    487{
    488	iosf_dbg = debugfs_create_dir("iosf_sb", NULL);
    489
    490	/* mdr */
    491	debugfs_create_x32("mdr", 0660, iosf_dbg, &dbg_mdr);
    492
    493	/* mcrx */
    494	debugfs_create_x32("mcrx", 0660, iosf_dbg, &dbg_mcrx);
    495
    496	/* mcr - initiates mailbox transaction */
    497	debugfs_create_file("mcr", 0660, iosf_dbg, &dbg_mcr, &iosf_mcr_fops);
    498}
    499
    500static void iosf_debugfs_init(void)
    501{
    502	iosf_sideband_debug_init();
    503}
    504
    505static void iosf_debugfs_remove(void)
    506{
    507	debugfs_remove_recursive(iosf_dbg);
    508}
    509#else
    510static inline void iosf_debugfs_init(void) { }
    511static inline void iosf_debugfs_remove(void) { }
    512#endif /* CONFIG_IOSF_MBI_DEBUG */
    513
    514static int iosf_mbi_probe(struct pci_dev *pdev,
    515			  const struct pci_device_id *dev_id)
    516{
    517	int ret;
    518
    519	ret = pci_enable_device(pdev);
    520	if (ret < 0) {
    521		dev_err(&pdev->dev, "error: could not enable device\n");
    522		return ret;
    523	}
    524
    525	mbi_pdev = pci_dev_get(pdev);
    526	iosf_mbi_sem_address = dev_id->driver_data;
    527
    528	return 0;
    529}
    530
    531static const struct pci_device_id iosf_mbi_pci_ids[] = {
    532	{ PCI_DEVICE_DATA(INTEL, BAYTRAIL, PUNIT_SEMAPHORE_BYT) },
    533	{ PCI_DEVICE_DATA(INTEL, BRASWELL, PUNIT_SEMAPHORE_CHT) },
    534	{ PCI_DEVICE_DATA(INTEL, QUARK_X1000, 0) },
    535	{ PCI_DEVICE_DATA(INTEL, TANGIER, 0) },
    536	{ 0, },
    537};
    538MODULE_DEVICE_TABLE(pci, iosf_mbi_pci_ids);
    539
    540static struct pci_driver iosf_mbi_pci_driver = {
    541	.name		= "iosf_mbi_pci",
    542	.probe		= iosf_mbi_probe,
    543	.id_table	= iosf_mbi_pci_ids,
    544};
    545
    546static int __init iosf_mbi_init(void)
    547{
    548	iosf_debugfs_init();
    549
    550	cpu_latency_qos_add_request(&iosf_mbi_pm_qos, PM_QOS_DEFAULT_VALUE);
    551
    552	return pci_register_driver(&iosf_mbi_pci_driver);
    553}
    554
    555static void __exit iosf_mbi_exit(void)
    556{
    557	iosf_debugfs_remove();
    558
    559	pci_unregister_driver(&iosf_mbi_pci_driver);
    560	pci_dev_put(mbi_pdev);
    561	mbi_pdev = NULL;
    562
    563	cpu_latency_qos_remove_request(&iosf_mbi_pm_qos);
    564}
    565
    566module_init(iosf_mbi_init);
    567module_exit(iosf_mbi_exit);
    568
    569MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
    570MODULE_DESCRIPTION("IOSF Mailbox Interface accessor");
    571MODULE_LICENSE("GPL v2");