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 35 static struct pci_dev *mbi_pdev; 36 static DEFINE_SPINLOCK(iosf_mbi_lock); 37 static DEFINE_MUTEX(iosf_mbi_punit_mutex); 38 static BLOCKING_NOTIFIER_HEAD(iosf_mbi_pmic_bus_access_notifier); 39 40 static 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 45 static 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 69 fail_read: 70 dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result); 71 return result; 72 } 73 74 static 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 98 fail_write: 99 dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result); 100 return result; 101 } 102 103 int 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 } 124 EXPORT_SYMBOL(iosf_mbi_read); 125 126 int 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 } 147 EXPORT_SYMBOL(iosf_mbi_write); 148 149 int 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 } 186 EXPORT_SYMBOL(iosf_mbi_modify); 187 188 bool iosf_mbi_available(void) 189 { 190 /* Mbi isn't hot-pluggable. No remove routine is provided */ 191 return mbi_pdev; 192 } 193 EXPORT_SYMBOL(iosf_mbi_available); 194 195 void iosf_mbi_punit_acquire(void) 196 { 197 mutex_lock(&iosf_mbi_punit_mutex); 198 } 199 EXPORT_SYMBOL(iosf_mbi_punit_acquire); 200 201 void iosf_mbi_punit_release(void) 202 { 203 mutex_unlock(&iosf_mbi_punit_mutex); 204 } 205 EXPORT_SYMBOL(iosf_mbi_punit_release); 206 207 int 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 } 219 EXPORT_SYMBOL(iosf_mbi_register_pmic_bus_access_notifier); 220 221 int iosf_mbi_unregister_pmic_bus_access_notifier_unlocked( 222 struct notifier_block *nb) 223 { 224 iosf_mbi_assert_punit_acquired(); 225 226 return blocking_notifier_chain_unregister( 227 &iosf_mbi_pmic_bus_access_notifier, nb); 228 } 229 EXPORT_SYMBOL(iosf_mbi_unregister_pmic_bus_access_notifier_unlocked); 230 231 int iosf_mbi_unregister_pmic_bus_access_notifier(struct notifier_block *nb) 232 { 233 int ret; 234 235 /* Wait for the bus to go inactive before unregistering */ 236 mutex_lock(&iosf_mbi_punit_mutex); 237 ret = iosf_mbi_unregister_pmic_bus_access_notifier_unlocked(nb); 238 mutex_unlock(&iosf_mbi_punit_mutex); 239 240 return ret; 241 } 242 EXPORT_SYMBOL(iosf_mbi_unregister_pmic_bus_access_notifier); 243 244 int iosf_mbi_call_pmic_bus_access_notifier_chain(unsigned long val, void *v) 245 { 246 return blocking_notifier_call_chain( 247 &iosf_mbi_pmic_bus_access_notifier, val, v); 248 } 249 EXPORT_SYMBOL(iosf_mbi_call_pmic_bus_access_notifier_chain); 250 251 void iosf_mbi_assert_punit_acquired(void) 252 { 253 WARN_ON(!mutex_is_locked(&iosf_mbi_punit_mutex)); 254 } 255 EXPORT_SYMBOL(iosf_mbi_assert_punit_acquired); 256 257 #ifdef CONFIG_IOSF_MBI_DEBUG 258 static u32 dbg_mdr; 259 static u32 dbg_mcr; 260 static u32 dbg_mcrx; 261 262 static int mcr_get(void *data, u64 *val) 263 { 264 *val = *(u32 *)data; 265 return 0; 266 } 267 268 static int mcr_set(void *data, u64 val) 269 { 270 u8 command = ((u32)val & 0xFF000000) >> 24, 271 port = ((u32)val & 0x00FF0000) >> 16, 272 offset = ((u32)val & 0x0000FF00) >> 8; 273 int err; 274 275 *(u32 *)data = val; 276 277 if (!capable(CAP_SYS_RAWIO)) 278 return -EACCES; 279 280 if (command & 1u) 281 err = iosf_mbi_write(port, 282 command, 283 dbg_mcrx | offset, 284 dbg_mdr); 285 else 286 err = iosf_mbi_read(port, 287 command, 288 dbg_mcrx | offset, 289 &dbg_mdr); 290 291 return err; 292 } 293 DEFINE_SIMPLE_ATTRIBUTE(iosf_mcr_fops, mcr_get, mcr_set , "%llx\n"); 294 295 static struct dentry *iosf_dbg; 296 297 static void iosf_sideband_debug_init(void) 298 { 299 struct dentry *d; 300 301 iosf_dbg = debugfs_create_dir("iosf_sb", NULL); 302 if (IS_ERR_OR_NULL(iosf_dbg)) 303 return; 304 305 /* mdr */ 306 d = debugfs_create_x32("mdr", 0660, iosf_dbg, &dbg_mdr); 307 if (!d) 308 goto cleanup; 309 310 /* mcrx */ 311 d = debugfs_create_x32("mcrx", 0660, iosf_dbg, &dbg_mcrx); 312 if (!d) 313 goto cleanup; 314 315 /* mcr - initiates mailbox tranaction */ 316 d = debugfs_create_file("mcr", 0660, iosf_dbg, &dbg_mcr, &iosf_mcr_fops); 317 if (!d) 318 goto cleanup; 319 320 return; 321 322 cleanup: 323 debugfs_remove_recursive(d); 324 } 325 326 static void iosf_debugfs_init(void) 327 { 328 iosf_sideband_debug_init(); 329 } 330 331 static void iosf_debugfs_remove(void) 332 { 333 debugfs_remove_recursive(iosf_dbg); 334 } 335 #else 336 static inline void iosf_debugfs_init(void) { } 337 static inline void iosf_debugfs_remove(void) { } 338 #endif /* CONFIG_IOSF_MBI_DEBUG */ 339 340 static int iosf_mbi_probe(struct pci_dev *pdev, 341 const struct pci_device_id *unused) 342 { 343 int ret; 344 345 ret = pci_enable_device(pdev); 346 if (ret < 0) { 347 dev_err(&pdev->dev, "error: could not enable device\n"); 348 return ret; 349 } 350 351 mbi_pdev = pci_dev_get(pdev); 352 return 0; 353 } 354 355 static const struct pci_device_id iosf_mbi_pci_ids[] = { 356 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_BAYTRAIL) }, 357 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_BRASWELL) }, 358 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_QUARK_X1000) }, 359 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_TANGIER) }, 360 { 0, }, 361 }; 362 MODULE_DEVICE_TABLE(pci, iosf_mbi_pci_ids); 363 364 static struct pci_driver iosf_mbi_pci_driver = { 365 .name = "iosf_mbi_pci", 366 .probe = iosf_mbi_probe, 367 .id_table = iosf_mbi_pci_ids, 368 }; 369 370 static int __init iosf_mbi_init(void) 371 { 372 iosf_debugfs_init(); 373 374 return pci_register_driver(&iosf_mbi_pci_driver); 375 } 376 377 static void __exit iosf_mbi_exit(void) 378 { 379 iosf_debugfs_remove(); 380 381 pci_unregister_driver(&iosf_mbi_pci_driver); 382 pci_dev_put(mbi_pdev); 383 mbi_pdev = NULL; 384 } 385 386 module_init(iosf_mbi_init); 387 module_exit(iosf_mbi_exit); 388 389 MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); 390 MODULE_DESCRIPTION("IOSF Mailbox Interface accessor"); 391 MODULE_LICENSE("GPL v2"); 392