2021-02-16 20:09:52 -08:00
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2020 Intel Corporation. All rights reserved. */
2021-05-13 22:22:05 -07:00
# include <linux/io-64-nonatomic-lo-hi.h>
2021-02-16 20:09:52 -08:00
# include <linux/device.h>
# include <linux/module.h>
2021-06-03 17:50:36 -07:00
# include <linux/pci.h>
2021-05-13 22:22:05 -07:00
# include "cxl.h"
2021-02-16 20:09:52 -08:00
/**
2021-05-13 22:22:00 -07:00
* DOC : cxl core
2021-02-16 20:09:52 -08:00
*
2021-05-13 22:22:00 -07:00
* The CXL core provides a sysfs hierarchy for control devices and a rendezvous
* point for cross - device interleave coordination through cxl ports .
2021-02-16 20:09:52 -08:00
*/
2021-05-13 22:22:00 -07:00
2021-05-13 22:22:05 -07:00
/**
2021-06-03 17:50:36 -07:00
* cxl_probe_device_regs ( ) - Detect CXL Device register blocks
2021-05-13 22:22:05 -07:00
* @ dev : Host device of the @ base mapping
* @ base : Mapping of CXL 2.0 8.2 .8 CXL Device Register Interface
2021-06-03 17:50:36 -07:00
* @ map : Map object describing the register block information found
*
* Probe for device register information and return it in map object .
2021-05-13 22:22:05 -07:00
*/
2021-06-03 17:50:36 -07:00
void cxl_probe_device_regs ( struct device * dev , void __iomem * base ,
struct cxl_device_reg_map * map )
2021-05-13 22:22:05 -07:00
{
int cap , cap_count ;
u64 cap_array ;
2021-06-03 17:50:36 -07:00
* map = ( struct cxl_device_reg_map ) { 0 } ;
2021-05-13 22:22:05 -07:00
cap_array = readq ( base + CXLDEV_CAP_ARRAY_OFFSET ) ;
if ( FIELD_GET ( CXLDEV_CAP_ARRAY_ID_MASK , cap_array ) ! =
CXLDEV_CAP_ARRAY_CAP_ID )
return ;
cap_count = FIELD_GET ( CXLDEV_CAP_ARRAY_COUNT_MASK , cap_array ) ;
for ( cap = 1 ; cap < = cap_count ; cap + + ) {
2021-06-03 17:50:36 -07:00
u32 offset , length ;
2021-05-13 22:22:05 -07:00
u16 cap_id ;
cap_id = FIELD_GET ( CXLDEV_CAP_HDR_CAP_ID_MASK ,
readl ( base + cap * 0x10 ) ) ;
offset = readl ( base + cap * 0x10 + 0x4 ) ;
2021-06-03 17:50:36 -07:00
length = readl ( base + cap * 0x10 + 0x8 ) ;
2021-05-13 22:22:05 -07:00
switch ( cap_id ) {
case CXLDEV_CAP_CAP_ID_DEVICE_STATUS :
dev_dbg ( dev , " found Status capability (0x%x) \n " , offset ) ;
2021-06-03 17:50:36 -07:00
map - > status . valid = true ;
map - > status . offset = offset ;
map - > status . size = length ;
2021-05-13 22:22:05 -07:00
break ;
case CXLDEV_CAP_CAP_ID_PRIMARY_MAILBOX :
dev_dbg ( dev , " found Mailbox capability (0x%x) \n " , offset ) ;
2021-06-03 17:50:36 -07:00
map - > mbox . valid = true ;
map - > mbox . offset = offset ;
map - > mbox . size = length ;
2021-05-13 22:22:05 -07:00
break ;
case CXLDEV_CAP_CAP_ID_SECONDARY_MAILBOX :
dev_dbg ( dev , " found Secondary Mailbox capability (0x%x) \n " , offset ) ;
break ;
case CXLDEV_CAP_CAP_ID_MEMDEV :
dev_dbg ( dev , " found Memory Device capability (0x%x) \n " , offset ) ;
2021-06-03 17:50:36 -07:00
map - > memdev . valid = true ;
map - > memdev . offset = offset ;
map - > memdev . size = length ;
2021-05-13 22:22:05 -07:00
break ;
default :
2021-05-20 13:48:52 -07:00
if ( cap_id > = 0x8000 )
dev_dbg ( dev , " Vendor cap ID: %#x offset: %#x \n " , cap_id , offset ) ;
else
dev_dbg ( dev , " Unknown cap ID: %#x offset: %#x \n " , cap_id , offset ) ;
2021-05-13 22:22:05 -07:00
break ;
}
}
}
2021-06-03 17:50:36 -07:00
EXPORT_SYMBOL_GPL ( cxl_probe_device_regs ) ;
2021-06-03 17:53:16 -07:00
static void __iomem * devm_cxl_iomap_block ( struct pci_dev * pdev ,
resource_size_t addr ,
resource_size_t length )
{
struct device * dev = & pdev - > dev ;
void __iomem * ret_val ;
struct resource * res ;
res = devm_request_mem_region ( dev , addr , length , pci_name ( pdev ) ) ;
if ( ! res ) {
resource_size_t end = addr + length - 1 ;
dev_err ( dev , " Failed to request region %pa-%pa \n " , & addr , & end ) ;
return NULL ;
}
ret_val = devm_ioremap ( dev , addr , length ) ;
if ( ! ret_val )
dev_err ( dev , " Failed to map region %pr \n " , res ) ;
return ret_val ;
}
2021-06-03 17:50:36 -07:00
int cxl_map_device_regs ( struct pci_dev * pdev ,
struct cxl_device_regs * regs ,
struct cxl_register_map * map )
{
resource_size_t phys_addr ;
phys_addr = pci_resource_start ( pdev , map - > barno ) ;
phys_addr + = map - > block_offset ;
if ( map - > device_map . status . valid ) {
resource_size_t addr ;
resource_size_t length ;
addr = phys_addr + map - > device_map . status . offset ;
length = map - > device_map . status . size ;
2021-06-03 17:53:16 -07:00
regs - > status = devm_cxl_iomap_block ( pdev , addr , length ) ;
if ( ! regs - > status )
return - ENOMEM ;
2021-06-03 17:50:36 -07:00
}
if ( map - > device_map . mbox . valid ) {
resource_size_t addr ;
resource_size_t length ;
addr = phys_addr + map - > device_map . mbox . offset ;
length = map - > device_map . mbox . size ;
2021-06-03 17:53:16 -07:00
regs - > mbox = devm_cxl_iomap_block ( pdev , addr , length ) ;
if ( ! regs - > mbox )
return - ENOMEM ;
2021-06-03 17:50:36 -07:00
}
if ( map - > device_map . memdev . valid ) {
resource_size_t addr ;
resource_size_t length ;
addr = phys_addr + map - > device_map . memdev . offset ;
length = map - > device_map . memdev . size ;
2021-06-03 17:53:16 -07:00
regs - > memdev = devm_cxl_iomap_block ( pdev , addr , length ) ;
if ( ! regs - > memdev )
return - ENOMEM ;
2021-06-03 17:50:36 -07:00
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( cxl_map_device_regs ) ;
2021-05-13 22:22:05 -07:00
2021-02-16 20:09:52 -08:00
struct bus_type cxl_bus_type = {
. name = " cxl " ,
} ;
EXPORT_SYMBOL_GPL ( cxl_bus_type ) ;
2021-05-13 22:22:00 -07:00
static __init int cxl_core_init ( void )
2021-02-16 20:09:52 -08:00
{
return bus_register ( & cxl_bus_type ) ;
}
2021-05-13 22:22:00 -07:00
static void cxl_core_exit ( void )
2021-02-16 20:09:52 -08:00
{
bus_unregister ( & cxl_bus_type ) ;
}
2021-05-13 22:22:00 -07:00
module_init ( cxl_core_init ) ;
module_exit ( cxl_core_exit ) ;
2021-02-16 20:09:52 -08:00
MODULE_LICENSE ( " GPL v2 " ) ;