mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 04:38:03 +00:00
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:
commit
d1d43fae06
@ -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.
|
||||
============================= ==== ================================
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user