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 38 static inline u32 iosf_mbi_form_mcr(u8 op, u8 port, u8 offset) 39 { 40 return (op << 24) | (port << 16) | (offset << 8) | MBI_ENABLE; 41 } 42 43 static 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 67 fail_read: 68 dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result); 69 return result; 70 } 71 72 static 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 96 fail_write: 97 dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result); 98 return result; 99 } 100 101 int 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 } 122 EXPORT_SYMBOL(iosf_mbi_read); 123 124 int 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 } 145 EXPORT_SYMBOL(iosf_mbi_write); 146 147 int 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 } 184 EXPORT_SYMBOL(iosf_mbi_modify); 185 186 bool iosf_mbi_available(void) 187 { 188 /* Mbi isn't hot-pluggable. No remove routine is provided */ 189 return mbi_pdev; 190 } 191 EXPORT_SYMBOL(iosf_mbi_available); 192 193 #ifdef CONFIG_IOSF_MBI_DEBUG 194 static u32 dbg_mdr; 195 static u32 dbg_mcr; 196 static u32 dbg_mcrx; 197 198 static int mcr_get(void *data, u64 *val) 199 { 200 *val = *(u32 *)data; 201 return 0; 202 } 203 204 static 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 } 229 DEFINE_SIMPLE_ATTRIBUTE(iosf_mcr_fops, mcr_get, mcr_set , "%llx\n"); 230 231 static struct dentry *iosf_dbg; 232 233 static 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 (!d) 244 goto cleanup; 245 246 /* mcrx */ 247 d = debugfs_create_x32("mcrx", 0660, iosf_dbg, &dbg_mcrx); 248 if (!d) 249 goto cleanup; 250 251 /* mcr - initiates mailbox tranaction */ 252 d = debugfs_create_file("mcr", 0660, iosf_dbg, &dbg_mcr, &iosf_mcr_fops); 253 if (!d) 254 goto cleanup; 255 256 return; 257 258 cleanup: 259 debugfs_remove_recursive(d); 260 } 261 262 static void iosf_debugfs_init(void) 263 { 264 iosf_sideband_debug_init(); 265 } 266 267 static void iosf_debugfs_remove(void) 268 { 269 debugfs_remove_recursive(iosf_dbg); 270 } 271 #else 272 static inline void iosf_debugfs_init(void) { } 273 static inline void iosf_debugfs_remove(void) { } 274 #endif /* CONFIG_IOSF_MBI_DEBUG */ 275 276 static 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 291 static 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 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_TANGIER) }, 296 { 0, }, 297 }; 298 MODULE_DEVICE_TABLE(pci, iosf_mbi_pci_ids); 299 300 static struct pci_driver iosf_mbi_pci_driver = { 301 .name = "iosf_mbi_pci", 302 .probe = iosf_mbi_probe, 303 .id_table = iosf_mbi_pci_ids, 304 }; 305 306 static int __init iosf_mbi_init(void) 307 { 308 iosf_debugfs_init(); 309 310 return pci_register_driver(&iosf_mbi_pci_driver); 311 } 312 313 static void __exit iosf_mbi_exit(void) 314 { 315 iosf_debugfs_remove(); 316 317 pci_unregister_driver(&iosf_mbi_pci_driver); 318 pci_dev_put(mbi_pdev); 319 mbi_pdev = NULL; 320 } 321 322 module_init(iosf_mbi_init); 323 module_exit(iosf_mbi_exit); 324 325 MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); 326 MODULE_DESCRIPTION("IOSF Mailbox Interface accessor"); 327 MODULE_LICENSE("GPL v2"); 328