HiSilicon driver updates for v6.13

- Add the sysfs to show all HCCS types used on the platform
 - Add the low power feature for HCCS by adjusting the lane number
 - Few cleanups and improvements: correct a PCC typo, verify the die, port
 information, base address and size, update the lane_mode to max_lane_num
 -----BEGIN PGP SIGNATURE-----
 
 iQJJBAABCgAzFiEEQeMYD8qOQTc1R/snC8hXbtmJZlwFAmcN3jAVHHh1d2VpNUBo
 aXNpbGljb24uY29tAAoJEAvIV27ZiWZcNBcP/2hYhGkVJynze6XdanfmDnMjUxKL
 CZQwDXEKOJIik9yLtJlPRhh8NuY+UTnsQquUJPY7XbAydjOjVFycwwQaQDiuC5zr
 JYpwgbM2cQ019fqEM8o8Y8mMAoo55bOmqJZ+gs31c7Zi5LeFNEHTXp+glaqTOXBQ
 Oc6H2exR52btPYZps+GfqVcePkaLVsMN/q6/akDTFO02oYALtinnm6R3X2ingEvH
 6+NNOOA/zAYKSKBLHvrquJzPHzCuFNK9D/UX9MkyIGoI3yo3Clxj4la97oevQ1hy
 oEvfgk0/VC/y5P4IUPAyXUIq7H4YeHurZ+gjw79AsH+Ky+WEKHOLnb13HMQAXDqv
 4PqwCE6QUJ0bCJbIxP78y96zzxtDXUqQABQGDtGB1qvDR+h5RiP1ELCWdn0v1DEZ
 R8/LnCIszYNHgQdjgsDgi5E8gttzGFFkMcYUaIwQ+CRd3I4qgowKXdkC2DISTOMS
 lNwp9tDcTouidya5Mxwzpjl0b3jv3Dpq1Vp2/Ck3CHLj1IVWuccFMKsZ4J9JZa7T
 KQjDDHWF1A/AK3N6u0ELxd0L7JHxM9hCwHkPsEU8EkP94FRSSPFPPDnuecx5sPNg
 knhUdEZnaKua3BO8kVZX0KJRZN/4LODytKseblv07sh1qMTfZhl9xTfxmTEzA+9X
 HBiPTnvh7omEaCfH
 =v3A3
 -----END PGP SIGNATURE-----
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEiK/NIGsWEZVxh/FrYKtH/8kJUicFAmck/WgACgkQYKtH/8kJ
 UicM1Q/8CzPTCxGEsuwuP3vc+BmSQV0J7sHIzR2XuvUyaxHEvfVCvPomP/eScPst
 bYbB0aGWcII0hfKjfXP4EuwqNWYD6j5rBkH7yuHeUYNh2XHUKFZZ67wIS1+TkbG5
 RVdibU3Bhm/vvJPDoDwcwH23El8At+pq39pT++8Yg6WXCuRwO5rYnnzYLb5xUhA8
 S5xYuvnTNqw/ci7N0d0mIOdGycv+5KMe+cATpWljHlwvqAGrcF1ZkeB2W5MVBnaf
 qRp/mEryohbpkxtIXTWcfGD7yBjIW4yMlpypMhInSr27aVFNYcBnzNVEwK/r1njA
 0hn2VoILhfmlkyEZGHfrdFoOuAf5qvux3s70K5WAnusY8qenmbcYnTihDzGsiBGV
 Udujhr7vk3roKaTyb6yU2sBSEZK1Cp0t4homWT2FpY7bJw0sJSqvU3RxsQK3bNA1
 iVJqyci/00TibSHrhjsEv4kgCOOniSPivK1J41j5CWwblnC5x29z+fjdw6Fk3Rjw
 pMk/K5wdYjQMpqmvoerGy+Tsmj5DNPOV5ZrVJ9GSlGJBCJ2Wke+f/CWUdPI9FIvi
 kyDKuQxlr6JL/r4c8gvey6MOLPEq471SyO6827n4XV9qsNagPffKZap+I5BZdhwM
 kfIoP1qcX2LW6zEGX6U+At/aEZx7BZNNlfSLZVDqbr7BKOXSsWg=
 =AbzF
 -----END PGP SIGNATURE-----

Merge tag 'hisi-drivers-for-6.13' of https://github.com/hisilicon/linux-hisi into arm/drivers

HiSilicon driver updates for v6.13

- Add the sysfs to show all HCCS types used on the platform
- Add the low power feature for HCCS by adjusting the lane number
- Few cleanups and improvements: correct a PCC typo, verify the die, port
information, base address and size, update the lane_mode to max_lane_num

* tag 'hisi-drivers-for-6.13' of https://github.com/hisilicon/linux-hisi:
  soc: hisilicon: kunpeng_hccs: Support low power feature for the specified HCCS type
  soc: hisilicon: kunpeng_hccs: Add used HCCS types sysfs
  soc: hisilicon: kunpeng_hccs: Fix the 'lane_mode' field name in port info structure to 'max_lane_num'
  soc: hisilicon: kunpeng_hccs: Add the check for base address and size of shared memory
  soc: hisilicon: kunpeng_hccs: Return failure on having not die or port information
  soc: hisilicon: kunpeng_hccs: Fix a PCC typo

Link: https://lore.kernel.org/r/671B3FBD.8050905@hisilicon.com
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Arnd Bergmann 2024-11-01 17:10:15 +01:00
commit d1d43fae06
4 changed files with 580 additions and 19 deletions

View File

@ -79,3 +79,48 @@ Description:
indicates a lane.
crc_err_cnt: (RO) CRC err count on this port.
============= ==== =============================================
What: /sys/devices/platform/HISI04Bx:00/used_types
Date: August 2024
KernelVersion: 6.12
Contact: Huisong Li <lihuisong@huawei.com>
Description:
This interface is used to show all HCCS types used on the
platform, like, HCCS-v1, HCCS-v2 and so on.
What: /sys/devices/platform/HISI04Bx:00/available_inc_dec_lane_types
What: /sys/devices/platform/HISI04Bx:00/dec_lane_of_type
What: /sys/devices/platform/HISI04Bx:00/inc_lane_of_type
Date: August 2024
KernelVersion: 6.12
Contact: Huisong Li <lihuisong@huawei.com>
Description:
These interfaces under /sys/devices/platform/HISI04Bx/ are
used to support the low power consumption feature of some
HCCS types by changing the number of lanes used. The interfaces
changing the number of lanes used are 'dec_lane_of_type' and
'inc_lane_of_type' which require root privileges. These
interfaces aren't exposed if no HCCS type on platform support
this feature. Please note that decreasing lane number is only
allowed if all the specified HCCS ports are not busy.
The low power consumption interfaces are as follows:
============================= ==== ================================
available_inc_dec_lane_types: (RO) available HCCS types (string) to
increase and decrease the number
of lane used, e.g. HCCS-v2.
dec_lane_of_type: (WO) input HCCS type supported
decreasing lane to decrease the
used lane number of all specified
HCCS type ports on platform to
the minimum.
You can query the 'cur_lane_num'
to get the minimum lane number
after executing successfully.
inc_lane_of_type: (WO) input HCCS type supported
increasing lane to increase the
used lane number of all specified
HCCS type ports on platform to
the full lane state.
============================= ==== ================================

View File

@ -13,9 +13,12 @@ config KUNPENG_HCCS
interconnection bus protocol.
The performance of application may be affected if some HCCS
ports are not in full lane status, have a large number of CRC
errors and so on.
errors and so on. This may support for reducing system power
consumption if there are HCCS ports supported low power feature
on platform.
Say M here if you want to include support for querying the
health status and port information of HCCS on Kunpeng SoC.
health status and port information of HCCS, or reducing system
power consumption on Kunpeng SoC.
endmenu

View File

@ -21,11 +21,22 @@
* - if all enabled ports are in linked
* - if all linked ports are in full lane
* - CRC error count sum
*
* - Retrieve all HCCS types used on the platform.
*
* - Support low power feature for all specified HCCS type ports, and
* provide the following interface:
* - query HCCS types supported increasing and decreasing lane number.
* - decrease lane number of all specified HCCS type ports on idle state.
* - increase lane number of all specified HCCS type ports.
*/
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/platform_device.h>
#include <linux/stringify.h>
#include <linux/sysfs.h>
#include <linux/types.h>
#include <acpi/pcc.h>
@ -53,6 +64,42 @@ static struct hccs_chip_info *kobj_to_chip_info(struct kobject *k)
return container_of(k, struct hccs_chip_info, kobj);
}
static struct hccs_dev *device_kobj_to_hccs_dev(struct kobject *k)
{
struct device *dev = container_of(k, struct device, kobj);
struct platform_device *pdev =
container_of(dev, struct platform_device, dev);
return platform_get_drvdata(pdev);
}
static char *hccs_port_type_to_name(struct hccs_dev *hdev, u8 type)
{
u16 i;
for (i = 0; i < hdev->used_type_num; i++) {
if (hdev->type_name_maps[i].type == type)
return hdev->type_name_maps[i].name;
}
return NULL;
}
static int hccs_name_to_port_type(struct hccs_dev *hdev,
const char *name, u8 *type)
{
u16 i;
for (i = 0; i < hdev->used_type_num; i++) {
if (strcmp(hdev->type_name_maps[i].name, name) == 0) {
*type = hdev->type_name_maps[i].type;
return 0;
}
}
return -EINVAL;
}
struct hccs_register_ctx {
struct device *dev;
u8 chan_id;
@ -144,7 +191,7 @@ static int hccs_register_pcc_channel(struct hccs_dev *hdev)
pcc_chan = pcc_mbox_request_channel(cl, hdev->chan_id);
if (IS_ERR(pcc_chan)) {
dev_err(dev, "PPC channel request failed.\n");
dev_err(dev, "PCC channel request failed.\n");
rc = -ENODEV;
goto out;
}
@ -170,15 +217,21 @@ static int hccs_register_pcc_channel(struct hccs_dev *hdev)
goto err_mbx_channel_free;
}
if (pcc_chan->shmem_base_addr) {
cl_info->pcc_comm_addr = ioremap(pcc_chan->shmem_base_addr,
pcc_chan->shmem_size);
if (!cl_info->pcc_comm_addr) {
dev_err(dev, "Failed to ioremap PCC communication region for channel-%u.\n",
hdev->chan_id);
rc = -ENOMEM;
goto err_mbx_channel_free;
}
if (!pcc_chan->shmem_base_addr ||
pcc_chan->shmem_size != HCCS_PCC_SHARE_MEM_BYTES) {
dev_err(dev, "The base address or size (%llu) of PCC communication region is invalid.\n",
pcc_chan->shmem_size);
rc = -EINVAL;
goto err_mbx_channel_free;
}
cl_info->pcc_comm_addr = ioremap(pcc_chan->shmem_base_addr,
pcc_chan->shmem_size);
if (!cl_info->pcc_comm_addr) {
dev_err(dev, "Failed to ioremap PCC communication region for channel-%u.\n",
hdev->chan_id);
rc = -ENOMEM;
goto err_mbx_channel_free;
}
return 0;
@ -451,6 +504,7 @@ static int hccs_query_all_die_info_on_platform(struct hccs_dev *hdev)
struct device *dev = hdev->dev;
struct hccs_chip_info *chip;
struct hccs_die_info *die;
bool has_die_info = false;
u8 i, j;
int ret;
@ -459,6 +513,7 @@ static int hccs_query_all_die_info_on_platform(struct hccs_dev *hdev)
if (!chip->die_num)
continue;
has_die_info = true;
chip->dies = devm_kzalloc(hdev->dev,
chip->die_num * sizeof(struct hccs_die_info),
GFP_KERNEL);
@ -480,7 +535,7 @@ static int hccs_query_all_die_info_on_platform(struct hccs_dev *hdev)
}
}
return 0;
return has_die_info ? 0 : -EINVAL;
}
static int hccs_get_bd_info(struct hccs_dev *hdev, u8 opcode,
@ -586,7 +641,7 @@ static int hccs_get_all_port_info_on_die(struct hccs_dev *hdev,
port = &die->ports[i];
port->port_id = attrs[i].port_id;
port->port_type = attrs[i].port_type;
port->lane_mode = attrs[i].lane_mode;
port->max_lane_num = attrs[i].max_lane_num;
port->enable = attrs[i].enable;
port->die = die;
}
@ -601,6 +656,7 @@ static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev)
struct device *dev = hdev->dev;
struct hccs_chip_info *chip;
struct hccs_die_info *die;
bool has_port_info = false;
u8 i, j;
int ret;
@ -611,6 +667,7 @@ static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev)
if (!die->port_num)
continue;
has_port_info = true;
die->ports = devm_kzalloc(dev,
die->port_num * sizeof(struct hccs_port_info),
GFP_KERNEL);
@ -629,7 +686,7 @@ static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev)
}
}
return 0;
return has_port_info ? 0 : -EINVAL;
}
static int hccs_get_hw_info(struct hccs_dev *hdev)
@ -660,6 +717,55 @@ static int hccs_get_hw_info(struct hccs_dev *hdev)
return 0;
}
static u16 hccs_calc_used_type_num(struct hccs_dev *hdev,
unsigned long *hccs_ver)
{
struct hccs_chip_info *chip;
struct hccs_port_info *port;
struct hccs_die_info *die;
u16 used_type_num = 0;
u16 i, j, k;
for (i = 0; i < hdev->chip_num; i++) {
chip = &hdev->chips[i];
for (j = 0; j < chip->die_num; j++) {
die = &chip->dies[j];
for (k = 0; k < die->port_num; k++) {
port = &die->ports[k];
set_bit(port->port_type, hccs_ver);
}
}
}
for_each_set_bit(i, hccs_ver, HCCS_IP_MAX + 1)
used_type_num++;
return used_type_num;
}
static int hccs_init_type_name_maps(struct hccs_dev *hdev)
{
DECLARE_BITMAP(hccs_ver, HCCS_IP_MAX + 1) = {};
unsigned int i;
u16 idx = 0;
hdev->used_type_num = hccs_calc_used_type_num(hdev, hccs_ver);
hdev->type_name_maps = devm_kcalloc(hdev->dev, hdev->used_type_num,
sizeof(struct hccs_type_name_map),
GFP_KERNEL);
if (!hdev->type_name_maps)
return -ENOMEM;
for_each_set_bit(i, hccs_ver, HCCS_IP_MAX + 1) {
hdev->type_name_maps[idx].type = i;
sprintf(hdev->type_name_maps[idx].name,
"%s%u", HCCS_IP_PREFIX, i);
idx++;
}
return 0;
}
static int hccs_query_port_link_status(struct hccs_dev *hdev,
const struct hccs_port_info *port,
struct hccs_link_status *link_status)
@ -820,7 +926,7 @@ static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
{
const struct hccs_port_info *port = kobj_to_port_info(kobj);
return sysfs_emit(buf, "HCCS-v%u\n", port->port_type);
return sysfs_emit(buf, "%s%u\n", HCCS_IP_PREFIX, port->port_type);
}
static struct kobj_attribute hccs_type_attr = __ATTR_RO(type);
@ -829,7 +935,7 @@ static ssize_t lane_mode_show(struct kobject *kobj, struct kobj_attribute *attr,
{
const struct hccs_port_info *port = kobj_to_port_info(kobj);
return sysfs_emit(buf, "x%u\n", port->lane_mode);
return sysfs_emit(buf, "x%u\n", port->max_lane_num);
}
static struct kobj_attribute lane_mode_attr = __ATTR_RO(lane_mode);
@ -1124,6 +1230,372 @@ static const struct kobj_type hccs_chip_type = {
.default_groups = hccs_chip_default_groups,
};
static int hccs_parse_pm_port_type(struct hccs_dev *hdev, const char *buf,
u8 *port_type)
{
char hccs_name[HCCS_NAME_MAX_LEN + 1] = "";
u8 type;
int ret;
ret = sscanf(buf, "%" __stringify(HCCS_NAME_MAX_LEN) "s", hccs_name);
if (ret != 1)
return -EINVAL;
ret = hccs_name_to_port_type(hdev, hccs_name, &type);
if (ret) {
dev_dbg(hdev->dev, "input invalid, please get the available types from 'used_types'.\n");
return ret;
}
if (type == HCCS_V2 && hdev->caps & HCCS_CAPS_HCCS_V2_PM) {
*port_type = type;
return 0;
}
dev_dbg(hdev->dev, "%s doesn't support for increasing and decreasing lane.\n",
hccs_name);
return -EOPNOTSUPP;
}
static int hccs_query_port_idle_status(struct hccs_dev *hdev,
struct hccs_port_info *port, u8 *idle)
{
const struct hccs_die_info *die = port->die;
const struct hccs_chip_info *chip = die->chip;
struct hccs_port_comm_req_param *req_param;
struct hccs_desc desc;
int ret;
hccs_init_req_desc(&desc);
req_param = (struct hccs_port_comm_req_param *)desc.req.data;
req_param->chip_id = chip->chip_id;
req_param->die_id = die->die_id;
req_param->port_id = port->port_id;
ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_IDLE_STATUS, &desc);
if (ret) {
dev_err(hdev->dev,
"get port idle status failed, ret = %d.\n", ret);
return ret;
}
*idle = *((u8 *)desc.rsp.data);
return 0;
}
static int hccs_get_all_spec_port_idle_sta(struct hccs_dev *hdev, u8 port_type,
bool *all_idle)
{
struct hccs_chip_info *chip;
struct hccs_port_info *port;
struct hccs_die_info *die;
int ret = 0;
u8 i, j, k;
u8 idle;
*all_idle = false;
for (i = 0; i < hdev->chip_num; i++) {
chip = &hdev->chips[i];
for (j = 0; j < chip->die_num; j++) {
die = &chip->dies[j];
for (k = 0; k < die->port_num; k++) {
port = &die->ports[k];
if (port->port_type != port_type)
continue;
ret = hccs_query_port_idle_status(hdev, port,
&idle);
if (ret) {
dev_err(hdev->dev,
"hccs%u on chip%u/die%u get idle status failed, ret = %d.\n",
k, i, j, ret);
return ret;
} else if (idle == 0) {
dev_info(hdev->dev, "hccs%u on chip%u/die%u is busy.\n",
k, i, j);
return 0;
}
}
}
}
*all_idle = true;
return 0;
}
static int hccs_get_all_spec_port_full_lane_sta(struct hccs_dev *hdev,
u8 port_type, bool *full_lane)
{
struct hccs_link_status status = {0};
struct hccs_chip_info *chip;
struct hccs_port_info *port;
struct hccs_die_info *die;
u8 i, j, k;
int ret;
*full_lane = false;
for (i = 0; i < hdev->chip_num; i++) {
chip = &hdev->chips[i];
for (j = 0; j < chip->die_num; j++) {
die = &chip->dies[j];
for (k = 0; k < die->port_num; k++) {
port = &die->ports[k];
if (port->port_type != port_type)
continue;
ret = hccs_query_port_link_status(hdev, port,
&status);
if (ret)
return ret;
if (status.lane_num != port->max_lane_num)
return 0;
}
}
}
*full_lane = true;
return 0;
}
static int hccs_prepare_inc_lane(struct hccs_dev *hdev, u8 type)
{
struct hccs_inc_lane_req_param *req_param;
struct hccs_desc desc;
int ret;
hccs_init_req_desc(&desc);
req_param = (struct hccs_inc_lane_req_param *)desc.req.data;
req_param->port_type = type;
req_param->opt_type = HCCS_PREPARE_INC_LANE;
ret = hccs_pcc_cmd_send(hdev, HCCS_PM_INC_LANE, &desc);
if (ret)
dev_err(hdev->dev, "prepare for increasing lane failed, ret = %d.\n",
ret);
return ret;
}
static int hccs_wait_serdes_adapt_completed(struct hccs_dev *hdev, u8 type)
{
#define HCCS_MAX_WAIT_CNT_FOR_ADAPT 10
#define HCCS_QUERY_ADAPT_RES_DELAY_MS 100
#define HCCS_SERDES_ADAPT_OK 0
struct hccs_inc_lane_req_param *req_param;
u8 wait_cnt = HCCS_MAX_WAIT_CNT_FOR_ADAPT;
struct hccs_desc desc;
u8 adapt_res;
int ret;
do {
hccs_init_req_desc(&desc);
req_param = (struct hccs_inc_lane_req_param *)desc.req.data;
req_param->port_type = type;
req_param->opt_type = HCCS_GET_ADAPT_RES;
ret = hccs_pcc_cmd_send(hdev, HCCS_PM_INC_LANE, &desc);
if (ret) {
dev_err(hdev->dev, "query adapting result failed, ret = %d.\n",
ret);
return ret;
}
adapt_res = *((u8 *)&desc.rsp.data);
if (adapt_res == HCCS_SERDES_ADAPT_OK)
return 0;
msleep(HCCS_QUERY_ADAPT_RES_DELAY_MS);
} while (--wait_cnt);
dev_err(hdev->dev, "wait for adapting completed timeout.\n");
return -ETIMEDOUT;
}
static int hccs_start_hpcs_retraining(struct hccs_dev *hdev, u8 type)
{
struct hccs_inc_lane_req_param *req_param;
struct hccs_desc desc;
int ret;
hccs_init_req_desc(&desc);
req_param = (struct hccs_inc_lane_req_param *)desc.req.data;
req_param->port_type = type;
req_param->opt_type = HCCS_START_RETRAINING;
ret = hccs_pcc_cmd_send(hdev, HCCS_PM_INC_LANE, &desc);
if (ret)
dev_err(hdev->dev, "start hpcs retraining failed, ret = %d.\n",
ret);
return ret;
}
static int hccs_start_inc_lane(struct hccs_dev *hdev, u8 type)
{
int ret;
ret = hccs_prepare_inc_lane(hdev, type);
if (ret)
return ret;
ret = hccs_wait_serdes_adapt_completed(hdev, type);
if (ret)
return ret;
return hccs_start_hpcs_retraining(hdev, type);
}
static int hccs_start_dec_lane(struct hccs_dev *hdev, u8 type)
{
struct hccs_desc desc;
u8 *port_type;
int ret;
hccs_init_req_desc(&desc);
port_type = (u8 *)desc.req.data;
*port_type = type;
ret = hccs_pcc_cmd_send(hdev, HCCS_PM_DEC_LANE, &desc);
if (ret)
dev_err(hdev->dev, "start to decrease lane failed, ret = %d.\n",
ret);
return ret;
}
static ssize_t dec_lane_of_type_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
struct hccs_dev *hdev = device_kobj_to_hccs_dev(kobj);
bool all_in_idle;
u8 port_type;
int ret;
ret = hccs_parse_pm_port_type(hdev, buf, &port_type);
if (ret)
return ret;
mutex_lock(&hdev->lock);
ret = hccs_get_all_spec_port_idle_sta(hdev, port_type, &all_in_idle);
if (ret)
goto out;
if (!all_in_idle) {
ret = -EBUSY;
dev_err(hdev->dev, "please don't decrese lanes on high load with %s, ret = %d.\n",
hccs_port_type_to_name(hdev, port_type), ret);
goto out;
}
ret = hccs_start_dec_lane(hdev, port_type);
out:
mutex_unlock(&hdev->lock);
return ret == 0 ? count : ret;
}
static struct kobj_attribute dec_lane_of_type_attr =
__ATTR(dec_lane_of_type, 0200, NULL, dec_lane_of_type_store);
static ssize_t inc_lane_of_type_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
struct hccs_dev *hdev = device_kobj_to_hccs_dev(kobj);
bool full_lane;
u8 port_type;
int ret;
ret = hccs_parse_pm_port_type(hdev, buf, &port_type);
if (ret)
return ret;
mutex_lock(&hdev->lock);
ret = hccs_get_all_spec_port_full_lane_sta(hdev, port_type, &full_lane);
if (ret || full_lane)
goto out;
ret = hccs_start_inc_lane(hdev, port_type);
out:
mutex_unlock(&hdev->lock);
return ret == 0 ? count : ret;
}
static struct kobj_attribute inc_lane_of_type_attr =
__ATTR(inc_lane_of_type, 0200, NULL, inc_lane_of_type_store);
static ssize_t available_inc_dec_lane_types_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
struct hccs_dev *hdev = device_kobj_to_hccs_dev(kobj);
if (hdev->caps & HCCS_CAPS_HCCS_V2_PM)
return sysfs_emit(buf, "%s\n",
hccs_port_type_to_name(hdev, HCCS_V2));
return -EINVAL;
}
static struct kobj_attribute available_inc_dec_lane_types_attr =
__ATTR(available_inc_dec_lane_types, 0444,
available_inc_dec_lane_types_show, NULL);
static ssize_t used_types_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
struct hccs_dev *hdev = device_kobj_to_hccs_dev(kobj);
int len = 0;
u16 i;
for (i = 0; i < hdev->used_type_num - 1; i++)
len += sysfs_emit(&buf[len], "%s ", hdev->type_name_maps[i].name);
len += sysfs_emit(&buf[len], "%s\n", hdev->type_name_maps[i].name);
return len;
}
static struct kobj_attribute used_types_attr =
__ATTR(used_types, 0444, used_types_show, NULL);
static void hccs_remove_misc_sysfs(struct hccs_dev *hdev)
{
sysfs_remove_file(&hdev->dev->kobj, &used_types_attr.attr);
if (!(hdev->caps & HCCS_CAPS_HCCS_V2_PM))
return;
sysfs_remove_file(&hdev->dev->kobj,
&available_inc_dec_lane_types_attr.attr);
sysfs_remove_file(&hdev->dev->kobj, &dec_lane_of_type_attr.attr);
sysfs_remove_file(&hdev->dev->kobj, &inc_lane_of_type_attr.attr);
}
static int hccs_add_misc_sysfs(struct hccs_dev *hdev)
{
int ret;
ret = sysfs_create_file(&hdev->dev->kobj, &used_types_attr.attr);
if (ret)
return ret;
if (!(hdev->caps & HCCS_CAPS_HCCS_V2_PM))
return 0;
ret = sysfs_create_file(&hdev->dev->kobj,
&available_inc_dec_lane_types_attr.attr);
if (ret)
goto used_types_remove;
ret = sysfs_create_file(&hdev->dev->kobj, &dec_lane_of_type_attr.attr);
if (ret)
goto inc_dec_lane_types_remove;
ret = sysfs_create_file(&hdev->dev->kobj, &inc_lane_of_type_attr.attr);
if (ret)
goto dec_lane_of_type_remove;
return 0;
dec_lane_of_type_remove:
sysfs_remove_file(&hdev->dev->kobj, &dec_lane_of_type_attr.attr);
inc_dec_lane_types_remove:
sysfs_remove_file(&hdev->dev->kobj,
&available_inc_dec_lane_types_attr.attr);
used_types_remove:
sysfs_remove_file(&hdev->dev->kobj, &used_types_attr.attr);
return ret;
}
static void hccs_remove_die_dir(struct hccs_die_info *die)
{
struct hccs_port_info *port;
@ -1158,6 +1630,8 @@ static void hccs_remove_topo_dirs(struct hccs_dev *hdev)
for (i = 0; i < hdev->chip_num; i++)
hccs_remove_chip_dir(&hdev->chips[i]);
hccs_remove_misc_sysfs(hdev);
}
static int hccs_create_hccs_dir(struct hccs_dev *hdev,
@ -1253,6 +1727,12 @@ static int hccs_create_topo_dirs(struct hccs_dev *hdev)
}
}
ret = hccs_add_misc_sysfs(hdev);
if (ret) {
dev_err(hdev->dev, "create misc sysfs interface failed, ret = %d\n", ret);
goto err;
}
return 0;
err:
for (k = 0; k < id; k++)
@ -1303,6 +1783,10 @@ static int hccs_probe(struct platform_device *pdev)
if (rc)
goto unregister_pcc_chan;
rc = hccs_init_type_name_maps(hdev);
if (rc)
goto unregister_pcc_chan;
rc = hccs_create_topo_dirs(hdev);
if (rc)
goto unregister_pcc_chan;

View File

@ -10,6 +10,19 @@
* | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 |P0 | P1 | P2 | P3 |
*/
enum hccs_port_type {
HCCS_V1 = 1,
HCCS_V2,
};
#define HCCS_IP_PREFIX "HCCS-v"
#define HCCS_IP_MAX 255
#define HCCS_NAME_MAX_LEN 9
struct hccs_type_name_map {
u8 type;
char name[HCCS_NAME_MAX_LEN + 1];
};
/*
* This value cannot be 255, otherwise the loop of the multi-BD communication
* case cannot end.
@ -19,7 +32,7 @@
struct hccs_port_info {
u8 port_id;
u8 port_type;
u8 lane_mode;
u8 max_lane_num;
bool enable; /* if the port is enabled */
struct kobject kobj;
bool dir_created;
@ -67,13 +80,18 @@ struct hccs_verspecific_data {
bool has_txdone_irq;
};
#define HCCS_CAPS_HCCS_V2_PM BIT_ULL(0)
struct hccs_dev {
struct device *dev;
struct acpi_device *acpi_dev;
const struct hccs_verspecific_data *verspec_data;
/* device capabilities from firmware, like HCCS_CAPS_xxx. */
u64 caps;
u8 chip_num;
struct hccs_chip_info *chips;
u16 used_type_num;
struct hccs_type_name_map *type_name_maps;
u8 chan_id;
struct mutex lock;
struct hccs_mbox_client_info cl_info;
@ -91,6 +109,9 @@ enum hccs_subcmd_type {
HCCS_GET_DIE_PORTS_LANE_STA,
HCCS_GET_DIE_PORTS_LINK_STA,
HCCS_GET_DIE_PORTS_CRC_ERR_CNT,
HCCS_GET_PORT_IDLE_STATUS,
HCCS_PM_DEC_LANE,
HCCS_PM_INC_LANE,
HCCS_SUB_CMD_MAX = 255,
};
@ -113,7 +134,7 @@ struct hccs_die_info_rsp_data {
struct hccs_port_attr {
u8 port_id;
u8 port_type;
u8 lane_mode;
u8 max_lane_num;
u8 enable : 1; /* if the port is enabled */
u16 rsv[2];
};
@ -134,6 +155,14 @@ struct hccs_port_comm_req_param {
u8 port_id;
};
#define HCCS_PREPARE_INC_LANE 1
#define HCCS_GET_ADAPT_RES 2
#define HCCS_START_RETRAINING 3
struct hccs_inc_lane_req_param {
u8 port_type;
u8 opt_type;
};
#define HCCS_PORT_RESET 1
#define HCCS_PORT_SETUP 2
#define HCCS_PORT_CONFIG 3