Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.12-rc4 376 lines 8.6 kB view raw
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#define PCI_DEVICE_ID_TANGIER 0x1170 34 35static struct pci_dev *mbi_pdev; 36static DEFINE_SPINLOCK(iosf_mbi_lock); 37static DEFINE_MUTEX(iosf_mbi_punit_mutex); 38static BLOCKING_NOTIFIER_HEAD(iosf_mbi_pmic_bus_access_notifier); 39 40static inline u32 iosf_mbi_form_mcr(u8 op, u8 port, u8 offset) 41{ 42 return (op << 24) | (port << 16) | (offset << 8) | MBI_ENABLE; 43} 44 45static int iosf_mbi_pci_read_mdr(u32 mcrx, u32 mcr, u32 *mdr) 46{ 47 int result; 48 49 if (!mbi_pdev) 50 return -ENODEV; 51 52 if (mcrx) { 53 result = pci_write_config_dword(mbi_pdev, MBI_MCRX_OFFSET, 54 mcrx); 55 if (result < 0) 56 goto fail_read; 57 } 58 59 result = pci_write_config_dword(mbi_pdev, MBI_MCR_OFFSET, mcr); 60 if (result < 0) 61 goto fail_read; 62 63 result = pci_read_config_dword(mbi_pdev, MBI_MDR_OFFSET, mdr); 64 if (result < 0) 65 goto fail_read; 66 67 return 0; 68 69fail_read: 70 dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result); 71 return result; 72} 73 74static int iosf_mbi_pci_write_mdr(u32 mcrx, u32 mcr, u32 mdr) 75{ 76 int result; 77 78 if (!mbi_pdev) 79 return -ENODEV; 80 81 result = pci_write_config_dword(mbi_pdev, MBI_MDR_OFFSET, mdr); 82 if (result < 0) 83 goto fail_write; 84 85 if (mcrx) { 86 result = pci_write_config_dword(mbi_pdev, MBI_MCRX_OFFSET, 87 mcrx); 88 if (result < 0) 89 goto fail_write; 90 } 91 92 result = pci_write_config_dword(mbi_pdev, MBI_MCR_OFFSET, mcr); 93 if (result < 0) 94 goto fail_write; 95 96 return 0; 97 98fail_write: 99 dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result); 100 return result; 101} 102 103int iosf_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr) 104{ 105 u32 mcr, mcrx; 106 unsigned long flags; 107 int ret; 108 109 /* Access to the GFX unit is handled by GPU code */ 110 if (port == BT_MBI_UNIT_GFX) { 111 WARN_ON(1); 112 return -EPERM; 113 } 114 115 mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO); 116 mcrx = offset & MBI_MASK_HI; 117 118 spin_lock_irqsave(&iosf_mbi_lock, flags); 119 ret = iosf_mbi_pci_read_mdr(mcrx, mcr, mdr); 120 spin_unlock_irqrestore(&iosf_mbi_lock, flags); 121 122 return ret; 123} 124EXPORT_SYMBOL(iosf_mbi_read); 125 126int iosf_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr) 127{ 128 u32 mcr, mcrx; 129 unsigned long flags; 130 int ret; 131 132 /* Access to the GFX unit is handled by GPU code */ 133 if (port == BT_MBI_UNIT_GFX) { 134 WARN_ON(1); 135 return -EPERM; 136 } 137 138 mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO); 139 mcrx = offset & MBI_MASK_HI; 140 141 spin_lock_irqsave(&iosf_mbi_lock, flags); 142 ret = iosf_mbi_pci_write_mdr(mcrx, mcr, mdr); 143 spin_unlock_irqrestore(&iosf_mbi_lock, flags); 144 145 return ret; 146} 147EXPORT_SYMBOL(iosf_mbi_write); 148 149int iosf_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask) 150{ 151 u32 mcr, mcrx; 152 u32 value; 153 unsigned long flags; 154 int ret; 155 156 /* Access to the GFX unit is handled by GPU code */ 157 if (port == BT_MBI_UNIT_GFX) { 158 WARN_ON(1); 159 return -EPERM; 160 } 161 162 mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO); 163 mcrx = offset & MBI_MASK_HI; 164 165 spin_lock_irqsave(&iosf_mbi_lock, flags); 166 167 /* Read current mdr value */ 168 ret = iosf_mbi_pci_read_mdr(mcrx, mcr & MBI_RD_MASK, &value); 169 if (ret < 0) { 170 spin_unlock_irqrestore(&iosf_mbi_lock, flags); 171 return ret; 172 } 173 174 /* Apply mask */ 175 value &= ~mask; 176 mdr &= mask; 177 value |= mdr; 178 179 /* Write back */ 180 ret = iosf_mbi_pci_write_mdr(mcrx, mcr | MBI_WR_MASK, value); 181 182 spin_unlock_irqrestore(&iosf_mbi_lock, flags); 183 184 return ret; 185} 186EXPORT_SYMBOL(iosf_mbi_modify); 187 188bool iosf_mbi_available(void) 189{ 190 /* Mbi isn't hot-pluggable. No remove routine is provided */ 191 return mbi_pdev; 192} 193EXPORT_SYMBOL(iosf_mbi_available); 194 195void iosf_mbi_punit_acquire(void) 196{ 197 mutex_lock(&iosf_mbi_punit_mutex); 198} 199EXPORT_SYMBOL(iosf_mbi_punit_acquire); 200 201void iosf_mbi_punit_release(void) 202{ 203 mutex_unlock(&iosf_mbi_punit_mutex); 204} 205EXPORT_SYMBOL(iosf_mbi_punit_release); 206 207int iosf_mbi_register_pmic_bus_access_notifier(struct notifier_block *nb) 208{ 209 int ret; 210 211 /* Wait for the bus to go inactive before registering */ 212 mutex_lock(&iosf_mbi_punit_mutex); 213 ret = blocking_notifier_chain_register( 214 &iosf_mbi_pmic_bus_access_notifier, nb); 215 mutex_unlock(&iosf_mbi_punit_mutex); 216 217 return ret; 218} 219EXPORT_SYMBOL(iosf_mbi_register_pmic_bus_access_notifier); 220 221int iosf_mbi_unregister_pmic_bus_access_notifier(struct notifier_block *nb) 222{ 223 int ret; 224 225 /* Wait for the bus to go inactive before unregistering */ 226 mutex_lock(&iosf_mbi_punit_mutex); 227 ret = blocking_notifier_chain_unregister( 228 &iosf_mbi_pmic_bus_access_notifier, nb); 229 mutex_unlock(&iosf_mbi_punit_mutex); 230 231 return ret; 232} 233EXPORT_SYMBOL(iosf_mbi_unregister_pmic_bus_access_notifier); 234 235int iosf_mbi_call_pmic_bus_access_notifier_chain(unsigned long val, void *v) 236{ 237 return blocking_notifier_call_chain( 238 &iosf_mbi_pmic_bus_access_notifier, val, v); 239} 240EXPORT_SYMBOL(iosf_mbi_call_pmic_bus_access_notifier_chain); 241 242#ifdef CONFIG_IOSF_MBI_DEBUG 243static u32 dbg_mdr; 244static u32 dbg_mcr; 245static u32 dbg_mcrx; 246 247static int mcr_get(void *data, u64 *val) 248{ 249 *val = *(u32 *)data; 250 return 0; 251} 252 253static int mcr_set(void *data, u64 val) 254{ 255 u8 command = ((u32)val & 0xFF000000) >> 24, 256 port = ((u32)val & 0x00FF0000) >> 16, 257 offset = ((u32)val & 0x0000FF00) >> 8; 258 int err; 259 260 *(u32 *)data = val; 261 262 if (!capable(CAP_SYS_RAWIO)) 263 return -EACCES; 264 265 if (command & 1u) 266 err = iosf_mbi_write(port, 267 command, 268 dbg_mcrx | offset, 269 dbg_mdr); 270 else 271 err = iosf_mbi_read(port, 272 command, 273 dbg_mcrx | offset, 274 &dbg_mdr); 275 276 return err; 277} 278DEFINE_SIMPLE_ATTRIBUTE(iosf_mcr_fops, mcr_get, mcr_set , "%llx\n"); 279 280static struct dentry *iosf_dbg; 281 282static void iosf_sideband_debug_init(void) 283{ 284 struct dentry *d; 285 286 iosf_dbg = debugfs_create_dir("iosf_sb", NULL); 287 if (IS_ERR_OR_NULL(iosf_dbg)) 288 return; 289 290 /* mdr */ 291 d = debugfs_create_x32("mdr", 0660, iosf_dbg, &dbg_mdr); 292 if (!d) 293 goto cleanup; 294 295 /* mcrx */ 296 d = debugfs_create_x32("mcrx", 0660, iosf_dbg, &dbg_mcrx); 297 if (!d) 298 goto cleanup; 299 300 /* mcr - initiates mailbox tranaction */ 301 d = debugfs_create_file("mcr", 0660, iosf_dbg, &dbg_mcr, &iosf_mcr_fops); 302 if (!d) 303 goto cleanup; 304 305 return; 306 307cleanup: 308 debugfs_remove_recursive(d); 309} 310 311static void iosf_debugfs_init(void) 312{ 313 iosf_sideband_debug_init(); 314} 315 316static void iosf_debugfs_remove(void) 317{ 318 debugfs_remove_recursive(iosf_dbg); 319} 320#else 321static inline void iosf_debugfs_init(void) { } 322static inline void iosf_debugfs_remove(void) { } 323#endif /* CONFIG_IOSF_MBI_DEBUG */ 324 325static int iosf_mbi_probe(struct pci_dev *pdev, 326 const struct pci_device_id *unused) 327{ 328 int ret; 329 330 ret = pci_enable_device(pdev); 331 if (ret < 0) { 332 dev_err(&pdev->dev, "error: could not enable device\n"); 333 return ret; 334 } 335 336 mbi_pdev = pci_dev_get(pdev); 337 return 0; 338} 339 340static const struct pci_device_id iosf_mbi_pci_ids[] = { 341 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_BAYTRAIL) }, 342 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_BRASWELL) }, 343 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_QUARK_X1000) }, 344 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_TANGIER) }, 345 { 0, }, 346}; 347MODULE_DEVICE_TABLE(pci, iosf_mbi_pci_ids); 348 349static struct pci_driver iosf_mbi_pci_driver = { 350 .name = "iosf_mbi_pci", 351 .probe = iosf_mbi_probe, 352 .id_table = iosf_mbi_pci_ids, 353}; 354 355static int __init iosf_mbi_init(void) 356{ 357 iosf_debugfs_init(); 358 359 return pci_register_driver(&iosf_mbi_pci_driver); 360} 361 362static void __exit iosf_mbi_exit(void) 363{ 364 iosf_debugfs_remove(); 365 366 pci_unregister_driver(&iosf_mbi_pci_driver); 367 pci_dev_put(mbi_pdev); 368 mbi_pdev = NULL; 369} 370 371module_init(iosf_mbi_init); 372module_exit(iosf_mbi_exit); 373 374MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); 375MODULE_DESCRIPTION("IOSF Mailbox Interface accessor"); 376MODULE_LICENSE("GPL v2");