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(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 } 233 EXPORT_SYMBOL(iosf_mbi_unregister_pmic_bus_access_notifier); 234 235 int 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 } 240 EXPORT_SYMBOL(iosf_mbi_call_pmic_bus_access_notifier_chain); 241 242 #ifdef CONFIG_IOSF_MBI_DEBUG 243 static u32 dbg_mdr; 244 static u32 dbg_mcr; 245 static u32 dbg_mcrx; 246 247 static int mcr_get(void *data, u64 *val) 248 { 249 *val = *(u32 *)data; 250 return 0; 251 } 252 253 static 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 } 278 DEFINE_SIMPLE_ATTRIBUTE(iosf_mcr_fops, mcr_get, mcr_set , "%llx\n"); 279 280 static struct dentry *iosf_dbg; 281 282 static 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 307 cleanup: 308 debugfs_remove_recursive(d); 309 } 310 311 static void iosf_debugfs_init(void) 312 { 313 iosf_sideband_debug_init(); 314 } 315 316 static void iosf_debugfs_remove(void) 317 { 318 debugfs_remove_recursive(iosf_dbg); 319 } 320 #else 321 static inline void iosf_debugfs_init(void) { } 322 static inline void iosf_debugfs_remove(void) { } 323 #endif /* CONFIG_IOSF_MBI_DEBUG */ 324 325 static 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 340 static 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 }; 347 MODULE_DEVICE_TABLE(pci, iosf_mbi_pci_ids); 348 349 static 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 355 static int __init iosf_mbi_init(void) 356 { 357 iosf_debugfs_init(); 358 359 return pci_register_driver(&iosf_mbi_pci_driver); 360 } 361 362 static 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 371 module_init(iosf_mbi_init); 372 module_exit(iosf_mbi_exit); 373 374 MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); 375 MODULE_DESCRIPTION("IOSF Mailbox Interface accessor"); 376 MODULE_LICENSE("GPL v2"); 377