linux/arch/x86/kernel/iosf_mbi.c
<<
>>
Prefs
   1/*
   2 * IOSF-SB MailBox Interface Driver
   3 * Copyright (c) 2013, Intel Corporation.
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms and conditions of the GNU General Public License,
   7 * version 2, as published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope it will be useful, but WITHOUT
  10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  12 * more details.
  13 *
  14 *
  15 * The IOSF-SB is a fabric bus available on Atom based SOC's that uses a
  16 * mailbox interface (MBI) to communicate with mutiple devices. This
  17 * driver implements access to this interface for those platforms that can
  18 * enumerate the device using PCI.
  19 */
  20
  21#include <linux/module.h>
  22#include <linux/init.h>
  23#include <linux/spinlock.h>
  24#include <linux/pci.h>
  25#include <linux/debugfs.h>
  26#include <linux/capability.h>
  27
  28#include <asm/iosf_mbi.h>
  29
  30#define PCI_DEVICE_ID_BAYTRAIL          0x0F00
  31#define PCI_DEVICE_ID_BRASWELL          0x2280
  32#define PCI_DEVICE_ID_QUARK_X1000       0x0958
  33
  34static DEFINE_SPINLOCK(iosf_mbi_lock);
  35
  36static inline u32 iosf_mbi_form_mcr(u8 op, u8 port, u8 offset)
  37{
  38        return (op << 24) | (port << 16) | (offset << 8) | MBI_ENABLE;
  39}
  40
  41static struct pci_dev *mbi_pdev;        /* one mbi device */
  42
  43static int iosf_mbi_pci_read_mdr(u32 mcrx, u32 mcr, u32 *mdr)
  44{
  45        int result;
  46
  47        if (!mbi_pdev)
  48                return -ENODEV;
  49
  50        if (mcrx) {
  51                result = pci_write_config_dword(mbi_pdev, MBI_MCRX_OFFSET,
  52                                                mcrx);
  53                if (result < 0)
  54                        goto fail_read;
  55        }
  56
  57        result = pci_write_config_dword(mbi_pdev, MBI_MCR_OFFSET, mcr);
  58        if (result < 0)
  59                goto fail_read;
  60
  61        result = pci_read_config_dword(mbi_pdev, MBI_MDR_OFFSET, mdr);
  62        if (result < 0)
  63                goto fail_read;
  64
  65        return 0;
  66
  67fail_read:
  68        dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result);
  69        return result;
  70}
  71
  72static int iosf_mbi_pci_write_mdr(u32 mcrx, u32 mcr, u32 mdr)
  73{
  74        int result;
  75
  76        if (!mbi_pdev)
  77                return -ENODEV;
  78
  79        result = pci_write_config_dword(mbi_pdev, MBI_MDR_OFFSET, mdr);
  80        if (result < 0)
  81                goto fail_write;
  82
  83        if (mcrx) {
  84                result = pci_write_config_dword(mbi_pdev, MBI_MCRX_OFFSET,
  85                                                mcrx);
  86                if (result < 0)
  87                        goto fail_write;
  88        }
  89
  90        result = pci_write_config_dword(mbi_pdev, MBI_MCR_OFFSET, mcr);
  91        if (result < 0)
  92                goto fail_write;
  93
  94        return 0;
  95
  96fail_write:
  97        dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result);
  98        return result;
  99}
 100
 101int iosf_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr)
 102{
 103        u32 mcr, mcrx;
 104        unsigned long flags;
 105        int ret;
 106
 107        /*Access to the GFX unit is handled by GPU code */
 108        if (port == BT_MBI_UNIT_GFX) {
 109                WARN_ON(1);
 110                return -EPERM;
 111        }
 112
 113        mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO);
 114        mcrx = offset & MBI_MASK_HI;
 115
 116        spin_lock_irqsave(&iosf_mbi_lock, flags);
 117        ret = iosf_mbi_pci_read_mdr(mcrx, mcr, mdr);
 118        spin_unlock_irqrestore(&iosf_mbi_lock, flags);
 119
 120        return ret;
 121}
 122EXPORT_SYMBOL(iosf_mbi_read);
 123
 124int iosf_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr)
 125{
 126        u32 mcr, mcrx;
 127        unsigned long flags;
 128        int ret;
 129
 130        /*Access to the GFX unit is handled by GPU code */
 131        if (port == BT_MBI_UNIT_GFX) {
 132                WARN_ON(1);
 133                return -EPERM;
 134        }
 135
 136        mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO);
 137        mcrx = offset & MBI_MASK_HI;
 138
 139        spin_lock_irqsave(&iosf_mbi_lock, flags);
 140        ret = iosf_mbi_pci_write_mdr(mcrx, mcr, mdr);
 141        spin_unlock_irqrestore(&iosf_mbi_lock, flags);
 142
 143        return ret;
 144}
 145EXPORT_SYMBOL(iosf_mbi_write);
 146
 147int iosf_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask)
 148{
 149        u32 mcr, mcrx;
 150        u32 value;
 151        unsigned long flags;
 152        int ret;
 153
 154        /*Access to the GFX unit is handled by GPU code */
 155        if (port == BT_MBI_UNIT_GFX) {
 156                WARN_ON(1);
 157                return -EPERM;
 158        }
 159
 160        mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO);
 161        mcrx = offset & MBI_MASK_HI;
 162
 163        spin_lock_irqsave(&iosf_mbi_lock, flags);
 164
 165        /* Read current mdr value */
 166        ret = iosf_mbi_pci_read_mdr(mcrx, mcr & MBI_RD_MASK, &value);
 167        if (ret < 0) {
 168                spin_unlock_irqrestore(&iosf_mbi_lock, flags);
 169                return ret;
 170        }
 171
 172        /* Apply mask */
 173        value &= ~mask;
 174        mdr &= mask;
 175        value |= mdr;
 176
 177        /* Write back */
 178        ret = iosf_mbi_pci_write_mdr(mcrx, mcr | MBI_WR_MASK, value);
 179
 180        spin_unlock_irqrestore(&iosf_mbi_lock, flags);
 181
 182        return ret;
 183}
 184EXPORT_SYMBOL(iosf_mbi_modify);
 185
 186bool iosf_mbi_available(void)
 187{
 188        /* Mbi isn't hot-pluggable. No remove routine is provided */
 189        return mbi_pdev;
 190}
 191EXPORT_SYMBOL(iosf_mbi_available);
 192
 193#ifdef CONFIG_IOSF_MBI_DEBUG
 194static u32      dbg_mdr;
 195static u32      dbg_mcr;
 196static u32      dbg_mcrx;
 197
 198static int mcr_get(void *data, u64 *val)
 199{
 200        *val = *(u32 *)data;
 201        return 0;
 202}
 203
 204static int mcr_set(void *data, u64 val)
 205{
 206        u8 command = ((u32)val & 0xFF000000) >> 24,
 207           port    = ((u32)val & 0x00FF0000) >> 16,
 208           offset  = ((u32)val & 0x0000FF00) >> 8;
 209        int err;
 210
 211        *(u32 *)data = val;
 212
 213        if (!capable(CAP_SYS_RAWIO))
 214                return -EACCES;
 215
 216        if (command & 1u)
 217                err = iosf_mbi_write(port,
 218                               command,
 219                               dbg_mcrx | offset,
 220                               dbg_mdr);
 221        else
 222                err = iosf_mbi_read(port,
 223                              command,
 224                              dbg_mcrx | offset,
 225                              &dbg_mdr);
 226
 227        return err;
 228}
 229DEFINE_SIMPLE_ATTRIBUTE(iosf_mcr_fops, mcr_get, mcr_set , "%llx\n");
 230
 231static struct dentry *iosf_dbg;
 232
 233static void iosf_sideband_debug_init(void)
 234{
 235        struct dentry *d;
 236
 237        iosf_dbg = debugfs_create_dir("iosf_sb", NULL);
 238        if (IS_ERR_OR_NULL(iosf_dbg))
 239                return;
 240
 241        /* mdr */
 242        d = debugfs_create_x32("mdr", 0660, iosf_dbg, &dbg_mdr);
 243        if (IS_ERR_OR_NULL(d))
 244                goto cleanup;
 245
 246        /* mcrx */
 247        debugfs_create_x32("mcrx", 0660, iosf_dbg, &dbg_mcrx);
 248        if (IS_ERR_OR_NULL(d))
 249                goto cleanup;
 250
 251        /* mcr - initiates mailbox tranaction */
 252        debugfs_create_file("mcr", 0660, iosf_dbg, &dbg_mcr, &iosf_mcr_fops);
 253        if (IS_ERR_OR_NULL(d))
 254                goto cleanup;
 255
 256        return;
 257
 258cleanup:
 259        debugfs_remove_recursive(d);
 260}
 261
 262static void iosf_debugfs_init(void)
 263{
 264        iosf_sideband_debug_init();
 265}
 266
 267static void iosf_debugfs_remove(void)
 268{
 269        debugfs_remove_recursive(iosf_dbg);
 270}
 271#else
 272static inline void iosf_debugfs_init(void) { }
 273static inline void iosf_debugfs_remove(void) { }
 274#endif /* CONFIG_IOSF_MBI_DEBUG */
 275
 276static int iosf_mbi_probe(struct pci_dev *pdev,
 277                          const struct pci_device_id *unused)
 278{
 279        int ret;
 280
 281        ret = pci_enable_device(pdev);
 282        if (ret < 0) {
 283                dev_err(&pdev->dev, "error: could not enable device\n");
 284                return ret;
 285        }
 286
 287        mbi_pdev = pci_dev_get(pdev);
 288        return 0;
 289}
 290
 291static const struct pci_device_id iosf_mbi_pci_ids[] = {
 292        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_BAYTRAIL) },
 293        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_BRASWELL) },
 294        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_QUARK_X1000) },
 295        { 0, },
 296};
 297MODULE_DEVICE_TABLE(pci, iosf_mbi_pci_ids);
 298
 299static struct pci_driver iosf_mbi_pci_driver = {
 300        .name           = "iosf_mbi_pci",
 301        .probe          = iosf_mbi_probe,
 302        .id_table       = iosf_mbi_pci_ids,
 303};
 304
 305static int __init iosf_mbi_init(void)
 306{
 307        iosf_debugfs_init();
 308
 309        return pci_register_driver(&iosf_mbi_pci_driver);
 310}
 311
 312static void __exit iosf_mbi_exit(void)
 313{
 314        iosf_debugfs_remove();
 315
 316        pci_unregister_driver(&iosf_mbi_pci_driver);
 317        if (mbi_pdev) {
 318                pci_dev_put(mbi_pdev);
 319                mbi_pdev = NULL;
 320        }
 321}
 322
 323module_init(iosf_mbi_init);
 324module_exit(iosf_mbi_exit);
 325
 326MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
 327MODULE_DESCRIPTION("IOSF Mailbox Interface accessor");
 328MODULE_LICENSE("GPL v2");
 329