| /* |
| * rar_register.c - An Intel Restricted Access Region register driver |
| * |
| * Copyright(c) 2009 Intel Corporation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of the |
| * License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
| * 02111-1307, USA. |
| * |
| * ------------------------------------------------------------------- |
| * 20091204 Mark Allyn <mark.a.allyn@intel.com> |
| * Ossama Othman <ossama.othman@intel.com> |
| * Cleanup per feedback from Alan Cox and Arjan Van De Ven |
| * |
| * 20090806 Ossama Othman <ossama.othman@intel.com> |
| * Return zero high address if upper 22 bits is zero. |
| * Cleaned up checkpatch errors. |
| * Clarified that driver is dealing with bus addresses. |
| * |
| * 20090702 Ossama Othman <ossama.othman@intel.com> |
| * Removed unnecessary include directives |
| * Cleaned up spinlocks. |
| * Cleaned up logging. |
| * Improved invalid parameter checks. |
| * Fixed and simplified RAR address retrieval and RAR locking |
| * code. |
| * |
| * 20090626 Mark Allyn <mark.a.allyn@intel.com> |
| * Initial publish |
| */ |
| |
| #define DEBUG 1 |
| |
| #include "rar_register.h" |
| |
| #include <linux/module.h> |
| #include <linux/pci.h> |
| #include <linux/spinlock.h> |
| #include <linux/device.h> |
| #include <linux/kernel.h> |
| |
| /* === Lincroft Message Bus Interface === */ |
| #define LNC_MCR_OFFSET 0xD0 /* Message Control Register */ |
| #define LNC_MDR_OFFSET 0xD4 /* Message Data Register */ |
| |
| /* Message Opcodes */ |
| #define LNC_MESSAGE_READ_OPCODE 0xD0 |
| #define LNC_MESSAGE_WRITE_OPCODE 0xE0 |
| |
| /* Message Write Byte Enables */ |
| #define LNC_MESSAGE_BYTE_WRITE_ENABLES 0xF |
| |
| /* B-unit Port */ |
| #define LNC_BUNIT_PORT 0x3 |
| |
| /* === Lincroft B-Unit Registers - Programmed by IA32 firmware === */ |
| #define LNC_BRAR0L 0x10 |
| #define LNC_BRAR0H 0x11 |
| #define LNC_BRAR1L 0x12 |
| #define LNC_BRAR1H 0x13 |
| /* Reserved for SeP */ |
| #define LNC_BRAR2L 0x14 |
| #define LNC_BRAR2H 0x15 |
| |
| /* Moorestown supports three restricted access regions. */ |
| #define MRST_NUM_RAR 3 |
| |
| /* RAR Bus Address Range */ |
| struct rar_addr { |
| dma_addr_t low; |
| dma_addr_t high; |
| }; |
| |
| /* |
| * We create one of these for each RAR |
| */ |
| struct client { |
| int (*callback)(unsigned long data); |
| unsigned long driver_priv; |
| bool busy; |
| }; |
| |
| static DEFINE_MUTEX(rar_mutex); |
| static DEFINE_MUTEX(lnc_reg_mutex); |
| |
| /* |
| * One per RAR device (currently only one device) |
| */ |
| struct rar_device { |
| struct rar_addr rar_addr[MRST_NUM_RAR]; |
| struct pci_dev *rar_dev; |
| bool registered; |
| bool allocated; |
| struct client client[MRST_NUM_RAR]; |
| }; |
| |
| /* Current platforms have only one rar_device for 3 rar regions */ |
| static struct rar_device my_rar_device; |
| |
| /* |
| * Abstract out multiple device support. Current platforms only |
| * have a single RAR device. |
| */ |
| |
| /** |
| * alloc_rar_device - return a new RAR structure |
| * |
| * Return a new (but not yet ready) RAR device object |
| */ |
| static struct rar_device *alloc_rar_device(void) |
| { |
| if (my_rar_device.allocated) |
| return NULL; |
| my_rar_device.allocated = 1; |
| return &my_rar_device; |
| } |
| |
| /** |
| * free_rar_device - free a RAR object |
| * @rar: the RAR device being freed |
| * |
| * Release a RAR object and any attached resources |
| */ |
| static void free_rar_device(struct rar_device *rar) |
| { |
| pci_dev_put(rar->rar_dev); |
| rar->allocated = 0; |
| } |
| |
| /** |
| * _rar_to_device - return the device handling this RAR |
| * @rar: RAR number |
| * @off: returned offset |
| * |
| * Internal helper for looking up RAR devices. This and alloc are the |
| * two functions that need touching to go to multiple RAR devices. |
| */ |
| static struct rar_device *_rar_to_device(int rar, int *off) |
| { |
| if (rar >= 0 && rar <= 3) { |
| *off = rar; |
| return &my_rar_device; |
| } |
| return NULL; |
| } |
| |
| |
| /** |
| * rar_to_device - return the device handling this RAR |
| * @rar: RAR number |
| * @off: returned offset |
| * |
| * Return the device this RAR maps to if one is present, otherwise |
| * returns NULL. Reports the offset relative to the base of this |
| * RAR device in off. |
| */ |
| static struct rar_device *rar_to_device(int rar, int *off) |
| { |
| struct rar_device *rar_dev = _rar_to_device(rar, off); |
| if (rar_dev == NULL || !rar_dev->registered) |
| return NULL; |
| return rar_dev; |
| } |
| |
| /** |
| * rar_to_client - return the client handling this RAR |
| * @rar: RAR number |
| * |
| * Return the client this RAR maps to if a mapping is known, otherwise |
| * returns NULL. |
| */ |
| static struct client *rar_to_client(int rar) |
| { |
| int idx; |
| struct rar_device *r = _rar_to_device(rar, &idx); |
| if (r != NULL) |
| return &r->client[idx]; |
| return NULL; |
| } |
| |
| /** |
| * rar_read_addr - retrieve a RAR mapping |
| * @pdev: PCI device for the RAR |
| * @offset: offset for message |
| * @addr: returned address |
| * |
| * Reads the address of a given RAR register. Returns 0 on success |
| * or an error code on failure. |
| */ |
| static int rar_read_addr(struct pci_dev *pdev, int offset, dma_addr_t *addr) |
| { |
| /* |
| * ======== The Lincroft Message Bus Interface ======== |
| * Lincroft registers may be obtained via PCI from |
| * the host bridge using the Lincroft Message Bus |
| * Interface. That message bus interface is generally |
| * comprised of two registers: a control register (MCR, 0xDO) |
| * and a data register (MDR, 0xD4). |
| * |
| * The MCR (message control register) format is the following: |
| * 1. [31:24]: Opcode |
| * 2. [23:16]: Port |
| * 3. [15:8]: Register Offset |
| * 4. [7:4]: Byte Enables (use 0xF to set all of these bits |
| * to 1) |
| * 5. [3:0]: reserved |
| * |
| * Read (0xD0) and write (0xE0) opcodes are written to the |
| * control register when reading and writing to Lincroft |
| * registers, respectively. |
| * |
| * We're interested in registers found in the Lincroft |
| * B-unit. The B-unit port is 0x3. |
| * |
| * The six B-unit RAR register offsets we use are listed |
| * earlier in this file. |
| * |
| * Lastly writing to the MCR register requires the "Byte |
| * enables" bits to be set to 1. This may be achieved by |
| * writing 0xF at bit 4. |
| * |
| * The MDR (message data register) format is the following: |
| * 1. [31:0]: Read/Write Data |
| * |
| * Data being read from this register is only available after |
| * writing the appropriate control message to the MCR |
| * register. |
| * |
| * Data being written to this register must be written before |
| * writing the appropriate control message to the MCR |
| * register. |
| */ |
| |
| int result; |
| u32 addr32; |
| |
| /* Construct control message */ |
| u32 const message = |
| (LNC_MESSAGE_READ_OPCODE << 24) |
| | (LNC_BUNIT_PORT << 16) |
| | (offset << 8) |
| | (LNC_MESSAGE_BYTE_WRITE_ENABLES << 4); |
| |
| dev_dbg(&pdev->dev, "Offset for 'get' LNC MSG is %x\n", offset); |
| |
| /* |
| * We synchronize access to the Lincroft MCR and MDR registers |
| * until BOTH the command is issued through the MCR register |
| * and the corresponding data is read from the MDR register. |
| * Otherwise a race condition would exist between accesses to |
| * both registers. |
| */ |
| |
| mutex_lock(&lnc_reg_mutex); |
| |
| /* Send the control message */ |
| result = pci_write_config_dword(pdev, LNC_MCR_OFFSET, message); |
| if (!result) { |
| /* Read back the address as a 32bit value */ |
| result = pci_read_config_dword(pdev, LNC_MDR_OFFSET, &addr32); |
| *addr = (dma_addr_t)addr32; |
| } |
| mutex_unlock(&lnc_reg_mutex); |
| return result; |
| } |
| |
| /** |
| * rar_set_addr - Set a RAR mapping |
| * @pdev: PCI device for the RAR |
| * @offset: offset for message |
| * @addr: address to set |
| * |
| * Sets the address of a given RAR register. Returns 0 on success |
| * or an error code on failure. |
| */ |
| static int rar_set_addr(struct pci_dev *pdev, |
| int offset, |
| dma_addr_t addr) |
| { |
| /* |
| * Data being written to this register must be written before |
| * writing the appropriate control message to the MCR |
| * register. |
| * See rar_get_addrs() for a description of the |
| * message bus interface being used here. |
| */ |
| |
| int result; |
| |
| /* Construct control message */ |
| u32 const message = (LNC_MESSAGE_WRITE_OPCODE << 24) |
| | (LNC_BUNIT_PORT << 16) |
| | (offset << 8) |
| | (LNC_MESSAGE_BYTE_WRITE_ENABLES << 4); |
| |
| /* |
| * We synchronize access to the Lincroft MCR and MDR registers |
| * until BOTH the command is issued through the MCR register |
| * and the corresponding data is read from the MDR register. |
| * Otherwise a race condition would exist between accesses to |
| * both registers. |
| */ |
| |
| mutex_lock(&lnc_reg_mutex); |
| |
| /* Send the control message */ |
| result = pci_write_config_dword(pdev, LNC_MDR_OFFSET, addr); |
| if (!result) |
| /* And address */ |
| result = pci_write_config_dword(pdev, LNC_MCR_OFFSET, message); |
| |
| mutex_unlock(&lnc_reg_mutex); |
| return result; |
| } |
| |
| /* |
| * rar_init_params - Initialize RAR parameters |
| * @rar: RAR device to initialise |
| * |
| * Initialize RAR parameters, such as bus addresses, etc. Returns 0 |
| * on success, or an error code on failure. |
| */ |
| static int init_rar_params(struct rar_device *rar) |
| { |
| struct pci_dev *pdev = rar->rar_dev; |
| unsigned int i; |
| int result = 0; |
| int offset = 0x10; /* RAR 0 to 2 in order low/high/low/high/... */ |
| |
| /* Retrieve RAR start and end bus addresses. |
| * Access the RAR registers through the Lincroft Message Bus |
| * Interface on PCI device: 00:00.0 Host bridge. |
| */ |
| |
| for (i = 0; i < MRST_NUM_RAR; ++i) { |
| struct rar_addr *addr = &rar->rar_addr[i]; |
| |
| result = rar_read_addr(pdev, offset++, &addr->low); |
| if (result != 0) |
| return result; |
| |
| result = rar_read_addr(pdev, offset++, &addr->high); |
| if (result != 0) |
| return result; |
| |
| |
| /* |
| * Only the upper 22 bits of the RAR addresses are |
| * stored in their corresponding RAR registers so we |
| * must set the lower 10 bits accordingly. |
| |
| * The low address has its lower 10 bits cleared, and |
| * the high address has all its lower 10 bits set, |
| * e.g.: |
| * low = 0x2ffffc00 |
| */ |
| |
| addr->low &= (dma_addr_t)0xfffffc00u; |
| |
| /* |
| * Set bits 9:0 on uppser address if bits 31:10 are non |
| * zero; otherwize clear all bits |
| */ |
| |
| if ((addr->high & 0xfffffc00u) == 0) |
| addr->high = 0; |
| else |
| addr->high |= 0x3ffu; |
| } |
| /* Done accessing the device. */ |
| |
| if (result == 0) { |
| for (i = 0; i != MRST_NUM_RAR; ++i) { |
| /* |
| * "BRAR" refers to the RAR registers in the |
| * Lincroft B-unit. |
| */ |
| dev_info(&pdev->dev, "BRAR[%u] bus address range = " |
| "[%lx, %lx]\n", i, |
| (unsigned long)rar->rar_addr[i].low, |
| (unsigned long)rar->rar_addr[i].high); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * rar_get_address - get the bus address in a RAR |
| * @start: return value of start address of block |
| * @end: return value of end address of block |
| * |
| * The rar_get_address function is used by other device drivers |
| * to obtain RAR address information on a RAR. It takes three |
| * parameters: |
| * |
| * The function returns a 0 upon success or an error if there is no RAR |
| * facility on this system. |
| */ |
| int rar_get_address(int rar_index, dma_addr_t *start, dma_addr_t *end) |
| { |
| int idx; |
| struct rar_device *rar = rar_to_device(rar_index, &idx); |
| |
| if (rar == NULL) { |
| WARN_ON(1); |
| return -ENODEV; |
| } |
| |
| *start = rar->rar_addr[idx].low; |
| *end = rar->rar_addr[idx].high; |
| return 0; |
| } |
| EXPORT_SYMBOL(rar_get_address); |
| |
| /** |
| * rar_lock - lock a RAR register |
| * @rar_index: RAR to lock (0-2) |
| * |
| * The rar_lock function is ued by other device drivers to lock an RAR. |
| * once a RAR is locked, it stays locked until the next system reboot. |
| * |
| * The function returns a 0 upon success or an error if there is no RAR |
| * facility on this system, or the locking fails |
| */ |
| int rar_lock(int rar_index) |
| { |
| struct rar_device *rar; |
| int result; |
| int idx; |
| dma_addr_t low, high; |
| |
| rar = rar_to_device(rar_index, &idx); |
| |
| if (rar == NULL) { |
| WARN_ON(1); |
| return -EINVAL; |
| } |
| |
| low = rar->rar_addr[idx].low & 0xfffffc00u; |
| high = rar->rar_addr[idx].high & 0xfffffc00u; |
| |
| /* |
| * Only allow I/O from the graphics and Langwell; |
| * not from the x86 processor |
| */ |
| |
| if (rar_index == RAR_TYPE_VIDEO) { |
| low |= 0x00000009; |
| high |= 0x00000015; |
| } else if (rar_index == RAR_TYPE_AUDIO) { |
| /* Only allow I/O from Langwell; nothing from x86 */ |
| low |= 0x00000008; |
| high |= 0x00000018; |
| } else |
| /* Read-only from all agents */ |
| high |= 0x00000018; |
| |
| /* |
| * Now program the register using the Lincroft message |
| * bus interface. |
| */ |
| result = rar_set_addr(rar->rar_dev, |
| 2 * idx, low); |
| |
| if (result == 0) |
| result = rar_set_addr(rar->rar_dev, |
| 2 * idx + 1, high); |
| |
| return result; |
| } |
| EXPORT_SYMBOL(rar_lock); |
| |
| /** |
| * register_rar - register a RAR handler |
| * @num: RAR we wish to register for |
| * @callback: function to call when RAR support is available |
| * @data: data to pass to this function |
| * |
| * The register_rar function is to used by other device drivers |
| * to ensure that this driver is ready. As we cannot be sure of |
| * the compile/execute order of drivers in ther kernel, it is |
| * best to give this driver a callback function to call when |
| * it is ready to give out addresses. The callback function |
| * would have those steps that continue the initialization of |
| * a driver that do require a valid RAR address. One of those |
| * steps would be to call rar_get_address() |
| * |
| * This function return 0 on success an error code on failure. |
| */ |
| int register_rar(int num, int (*callback)(unsigned long data), |
| unsigned long data) |
| { |
| /* For now we hardcode a single RAR device */ |
| struct rar_device *rar; |
| struct client *c; |
| int idx; |
| int retval = 0; |
| |
| mutex_lock(&rar_mutex); |
| |
| /* Do we have a client mapping for this RAR number ? */ |
| c = rar_to_client(num); |
| if (c == NULL) { |
| retval = -ERANGE; |
| goto done; |
| } |
| /* Is it claimed ? */ |
| if (c->busy) { |
| retval = -EBUSY; |
| goto done; |
| } |
| c->busy = 1; |
| |
| /* See if we have a handler for this RAR yet, if we do then fire it */ |
| rar = rar_to_device(num, &idx); |
| |
| if (rar) { |
| /* |
| * if the driver already registered, then we can simply |
| * call the callback right now |
| */ |
| (*callback)(data); |
| goto done; |
| } |
| |
| /* Arrange to be called back when the hardware is found */ |
| c->callback = callback; |
| c->driver_priv = data; |
| done: |
| mutex_unlock(&rar_mutex); |
| return retval; |
| } |
| EXPORT_SYMBOL(register_rar); |
| |
| /** |
| * unregister_rar - release a RAR allocation |
| * @num: RAR number |
| * |
| * Releases a RAR allocation, or pending allocation. If a callback is |
| * pending then this function will either complete before the unregister |
| * returns or not at all. |
| */ |
| |
| void unregister_rar(int num) |
| { |
| struct client *c; |
| |
| mutex_lock(&rar_mutex); |
| c = rar_to_client(num); |
| if (c == NULL || !c->busy) |
| WARN_ON(1); |
| else |
| c->busy = 0; |
| mutex_unlock(&rar_mutex); |
| } |
| EXPORT_SYMBOL(unregister_rar); |
| |
| /** |
| * rar_callback - Process callbacks |
| * @rar: new RAR device |
| * |
| * Process the callbacks for a newly found RAR device. |
| */ |
| |
| static void rar_callback(struct rar_device *rar) |
| { |
| struct client *c = &rar->client[0]; |
| int i; |
| |
| mutex_lock(&rar_mutex); |
| |
| rar->registered = 1; /* Ensure no more callbacks queue */ |
| |
| for (i = 0; i < MRST_NUM_RAR; i++) { |
| if (c->callback && c->busy) { |
| c->callback(c->driver_priv); |
| c->callback = NULL; |
| } |
| c++; |
| } |
| mutex_unlock(&rar_mutex); |
| } |
| |
| /** |
| * rar_probe - PCI probe callback |
| * @dev: PCI device |
| * @id: matching entry in the match table |
| * |
| * A RAR device has been discovered. Initialise it and if successful |
| * process any pending callbacks that can now be completed. |
| */ |
| static int rar_probe(struct pci_dev *dev, const struct pci_device_id *id) |
| { |
| int error; |
| struct rar_device *rar; |
| |
| dev_dbg(&dev->dev, "PCI probe starting\n"); |
| |
| rar = alloc_rar_device(); |
| if (rar == NULL) |
| return -EBUSY; |
| |
| /* Enable the device */ |
| error = pci_enable_device(dev); |
| if (error) { |
| dev_err(&dev->dev, |
| "Error enabling RAR register PCI device\n"); |
| goto end_function; |
| } |
| |
| /* Fill in the rar_device structure */ |
| rar->rar_dev = pci_dev_get(dev); |
| pci_set_drvdata(dev, rar); |
| |
| /* |
| * Initialize the RAR parameters, which have to be retrieved |
| * via the message bus interface. |
| */ |
| error = init_rar_params(rar); |
| if (error) { |
| pci_disable_device(dev); |
| dev_err(&dev->dev, "Error retrieving RAR addresses\n"); |
| goto end_function; |
| } |
| /* now call anyone who has registered (using callbacks) */ |
| rar_callback(rar); |
| return 0; |
| end_function: |
| free_rar_device(rar); |
| return error; |
| } |
| |
| const struct pci_device_id rar_pci_id_tbl[] = { |
| { PCI_VDEVICE(INTEL, 0x4110) }, |
| { 0 } |
| }; |
| |
| MODULE_DEVICE_TABLE(pci, rar_pci_id_tbl); |
| |
| const struct pci_device_id *my_id_table = rar_pci_id_tbl; |
| |
| /* field for registering driver to PCI device */ |
| static struct pci_driver rar_pci_driver = { |
| .name = "rar_register_driver", |
| .id_table = rar_pci_id_tbl, |
| .probe = rar_probe, |
| /* Cannot be unplugged - no remove */ |
| }; |
| |
| static int __init rar_init_handler(void) |
| { |
| return pci_register_driver(&rar_pci_driver); |
| } |
| |
| static void __exit rar_exit_handler(void) |
| { |
| pci_unregister_driver(&rar_pci_driver); |
| } |
| |
| module_init(rar_init_handler); |
| module_exit(rar_exit_handler); |
| |
| MODULE_LICENSE("GPL"); |
| MODULE_DESCRIPTION("Intel Restricted Access Region Register Driver"); |