2022-02-02 00:07:51 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
# include <linux/device.h>
# include <linux/module.h>
# include <linux/slab.h>
# include "cxlmem.h"
# include "cxlpci.h"
/**
* DOC : cxl port
*
* The port driver enumerates dport via PCI and scans for HDM
* ( Host - managed - Device - Memory ) decoder resources via the
* @ component_reg_phys value passed in by the agent that registered the
* port . All descendant ports of a CXL root port ( described by platform
* firmware ) are managed in this drivers context . Each driver instance
* is responsible for tearing down the driver context of immediate
* descendant ports . The locking for this is validated by
* CONFIG_PROVE_CXL_LOCKING .
*
* The primary service this driver provides is presenting APIs to other
* drivers to utilize the decoders , and indicating to userspace ( via bind
* status ) the connectivity of the CXL . mem protocol throughout the
* PCIe topology .
*/
2022-02-04 18:18:31 +03:00
static void schedule_detach ( void * cxlmd )
{
schedule_cxl_memdev_detach ( cxlmd ) ;
}
2022-02-02 00:07:51 +03:00
static int cxl_port_probe ( struct device * dev )
{
2023-02-14 22:41:13 +03:00
struct cxl_endpoint_dvsec_info info = { 0 } ;
2022-02-02 00:07:51 +03:00
struct cxl_port * port = to_cxl_port ( dev ) ;
2023-02-14 22:41:13 +03:00
bool is_ep = is_cxl_endpoint ( port ) ;
struct cxl_dev_state * cxlds ;
struct cxl_memdev * cxlmd ;
2022-02-02 00:07:51 +03:00
struct cxl_hdm * cxlhdm ;
int rc ;
2023-02-14 22:41:13 +03:00
if ( is_ep ) {
cxlmd = to_cxl_memdev ( port - > uport ) ;
cxlds = cxlmd - > cxlds ;
rc = cxl_dvsec_rr_decode ( cxlds - > dev , cxlds - > cxl_dvsec , & info ) ;
if ( rc < 0 )
return rc ;
} else {
2022-05-19 02:35:17 +03:00
rc = devm_cxl_port_enumerate_dports ( port ) ;
if ( rc < 0 )
return rc ;
if ( rc = = 1 )
return devm_cxl_add_passthrough_decoder ( port ) ;
}
cxlhdm = devm_cxl_setup_hdm ( port ) ;
if ( IS_ERR ( cxlhdm ) )
return PTR_ERR ( cxlhdm ) ;
2023-02-14 22:41:13 +03:00
if ( is_ep ) {
2022-07-19 23:52:49 +03:00
/* Cache the data early to ensure is_visible() works */
read_cdat_data ( port ) ;
2022-02-04 18:18:31 +03:00
get_device ( & cxlmd - > dev ) ;
2022-02-03 07:02:06 +03:00
rc = devm_add_action_or_reset ( dev , schedule_detach , cxlmd ) ;
if ( rc )
return rc ;
2022-05-19 02:35:11 +03:00
2023-02-14 22:41:13 +03:00
rc = cxl_hdm_decode_init ( cxlds , cxlhdm , & info ) ;
2022-05-19 02:35:11 +03:00
if ( rc )
return rc ;
rc = cxl_await_media_ready ( cxlds ) ;
if ( rc ) {
dev_err ( dev , " Media not active (%d) \n " , rc ) ;
return rc ;
}
2022-02-04 18:18:31 +03:00
}
2022-02-02 00:23:14 +03:00
rc = devm_cxl_enumerate_decoders ( cxlhdm ) ;
2022-02-02 00:07:51 +03:00
if ( rc ) {
dev_err ( dev , " Couldn't enumerate decoders (%d) \n " , rc ) ;
return rc ;
}
return 0 ;
}
2022-07-19 23:52:49 +03:00
static ssize_t CDAT_read ( struct file * filp , struct kobject * kobj ,
struct bin_attribute * bin_attr , char * buf ,
loff_t offset , size_t count )
{
struct device * dev = kobj_to_dev ( kobj ) ;
struct cxl_port * port = to_cxl_port ( dev ) ;
if ( ! port - > cdat_available )
return - ENXIO ;
if ( ! port - > cdat . table )
return 0 ;
return memory_read_from_buffer ( buf , count , & offset ,
port - > cdat . table ,
port - > cdat . length ) ;
}
static BIN_ATTR_ADMIN_RO ( CDAT , 0 ) ;
static umode_t cxl_port_bin_attr_is_visible ( struct kobject * kobj ,
struct bin_attribute * attr , int i )
{
struct device * dev = kobj_to_dev ( kobj ) ;
struct cxl_port * port = to_cxl_port ( dev ) ;
if ( ( attr = = & bin_attr_CDAT ) & & port - > cdat_available )
return attr - > attr . mode ;
return 0 ;
}
static struct bin_attribute * cxl_cdat_bin_attributes [ ] = {
& bin_attr_CDAT ,
NULL ,
} ;
static struct attribute_group cxl_cdat_attribute_group = {
. bin_attrs = cxl_cdat_bin_attributes ,
. is_bin_visible = cxl_port_bin_attr_is_visible ,
} ;
static const struct attribute_group * cxl_port_attribute_groups [ ] = {
& cxl_cdat_attribute_group ,
NULL ,
} ;
2022-02-02 00:07:51 +03:00
static struct cxl_driver cxl_port_driver = {
. name = " cxl_port " ,
. probe = cxl_port_probe ,
. id = CXL_DEVICE_PORT ,
2022-07-19 23:52:49 +03:00
. drv = {
. dev_groups = cxl_port_attribute_groups ,
} ,
2022-02-02 00:07:51 +03:00
} ;
module_cxl_driver ( cxl_port_driver ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_IMPORT_NS ( CXL ) ;
MODULE_ALIAS_CXL ( CXL_DEVICE_PORT ) ;