Commit 57d104c1 authored by Subhash Jadavani's avatar Subhash Jadavani Committed by Christoph Hellwig

ufs: add UFS power management support

This patch adds support for UFS device and UniPro link power management
during runtime/system PM.

Main idea is to define multiple UFS low power levels based on UFS device
and UFS link power states. This would allow any specific platform or pci
driver to choose the best suited low power level during runtime and
system suspend based on their power goals.

bkops handlig:
To put the UFS device in sleep state when bkops is disabled, first query
the bkops status from the device and enable bkops on device only if
device needs time to perform the bkops.

START_STOP handling:
Before sending START_STOP_UNIT to the device well-known logical unit
(w-lun) to make sure that the device w-lun unit attention condition is
cleared.

Write protection:
UFS device specification allows LUs to be write protected, either
permanently or power on write protected. If any LU is power on write
protected and if the card is power cycled (by powering off VCCQ and/or
VCC rails), LU's write protect status would be lost. So this means those
LUs can be written now. To ensures that UFS device is power cycled only
if the power on protect is not set for any of the LUs, check if power on
write protect is set and if device is in sleep/power-off state & link in
inactive state (Hibern8 or OFF state).
If none of the Logical Units on UFS device is power on write protected
then all UFS device power rails (VCC, VCCQ & VCCQ2) can be turned off if
UFS device is in power-off state and UFS link is in OFF state. But current
implementation would disable all device power rails even if UFS link is
not in OFF state.

Low power mode:
If UFS link is in OFF state then UFS host controller can be power collapsed
to avoid leakage current from it. Note that if UFS host controller is power
collapsed, full UFS reinitialization will be required on resume to
re-establish the link between host and device.
Signed-off-by: default avatarSubhash Jadavani <subhashj@codeaurora.org>
Signed-off-by: default avatarDolev Raviv <draviv@codeaurora.org>
Signed-off-by: default avatarSujit Reddy Thumma <sthumma@codeaurora.org>
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
parent 0ce147d4
...@@ -129,6 +129,7 @@ enum { ...@@ -129,6 +129,7 @@ enum {
/* Flag idn for Query Requests*/ /* Flag idn for Query Requests*/
enum flag_idn { enum flag_idn {
QUERY_FLAG_IDN_FDEVICEINIT = 0x01, QUERY_FLAG_IDN_FDEVICEINIT = 0x01,
QUERY_FLAG_IDN_PWR_ON_WPE = 0x03,
QUERY_FLAG_IDN_BKOPS_EN = 0x04, QUERY_FLAG_IDN_BKOPS_EN = 0x04,
}; };
...@@ -194,6 +195,18 @@ enum unit_desc_param { ...@@ -194,6 +195,18 @@ enum unit_desc_param {
UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22, UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22,
}; };
/*
* Logical Unit Write Protect
* 00h: LU not write protected
* 01h: LU write protected when fPowerOnWPEn =1
* 02h: LU permanently write protected when fPermanentWPEn =1
*/
enum ufs_lu_wp_type {
UFS_LU_NO_WP = 0x00,
UFS_LU_POWER_ON_WP = 0x01,
UFS_LU_PERM_WP = 0x02,
};
/* bActiveICCLevel parameter current units */ /* bActiveICCLevel parameter current units */
enum { enum {
UFSHCD_NANO_AMP = 0, UFSHCD_NANO_AMP = 0,
...@@ -226,11 +239,12 @@ enum { ...@@ -226,11 +239,12 @@ enum {
}; };
/* Background operation status */ /* Background operation status */
enum { enum bkops_status {
BKOPS_STATUS_NO_OP = 0x0, BKOPS_STATUS_NO_OP = 0x0,
BKOPS_STATUS_NON_CRITICAL = 0x1, BKOPS_STATUS_NON_CRITICAL = 0x1,
BKOPS_STATUS_PERF_IMPACT = 0x2, BKOPS_STATUS_PERF_IMPACT = 0x2,
BKOPS_STATUS_CRITICAL = 0x3, BKOPS_STATUS_CRITICAL = 0x3,
BKOPS_STATUS_MAX = BKOPS_STATUS_CRITICAL,
}; };
/* UTP QUERY Transaction Specific Fields OpCode */ /* UTP QUERY Transaction Specific Fields OpCode */
...@@ -291,6 +305,14 @@ enum { ...@@ -291,6 +305,14 @@ enum {
UPIU_TASK_MANAGEMENT_FUNC_FAILED = 0x05, UPIU_TASK_MANAGEMENT_FUNC_FAILED = 0x05,
UPIU_INCORRECT_LOGICAL_UNIT_NO = 0x09, UPIU_INCORRECT_LOGICAL_UNIT_NO = 0x09,
}; };
/* UFS device power modes */
enum ufs_dev_pwr_mode {
UFS_ACTIVE_PWR_MODE = 1,
UFS_SLEEP_PWR_MODE = 2,
UFS_POWERDOWN_PWR_MODE = 3,
};
/** /**
* struct utp_upiu_header - UPIU header structure * struct utp_upiu_header - UPIU header structure
* @dword_0: UPIU header DW-0 * @dword_0: UPIU header DW-0
...@@ -437,6 +459,12 @@ struct ufs_query_res { ...@@ -437,6 +459,12 @@ struct ufs_query_res {
#define UFS_VREG_VCCQ2_MIN_UV 1650000 /* uV */ #define UFS_VREG_VCCQ2_MIN_UV 1650000 /* uV */
#define UFS_VREG_VCCQ2_MAX_UV 1950000 /* uV */ #define UFS_VREG_VCCQ2_MAX_UV 1950000 /* uV */
/*
* VCCQ & VCCQ2 current requirement when UFS device is in sleep state
* and link is in Hibern8 state.
*/
#define UFS_VREG_LPM_LOAD_UA 1000 /* uA */
struct ufs_vreg { struct ufs_vreg {
struct regulator *reg; struct regulator *reg;
const char *name; const char *name;
...@@ -454,4 +482,10 @@ struct ufs_vreg_info { ...@@ -454,4 +482,10 @@ struct ufs_vreg_info {
struct ufs_vreg *vdd_hba; struct ufs_vreg *vdd_hba;
}; };
struct ufs_dev_info {
bool f_power_on_wp_en;
/* Keeps information if any of the LU is power on write protected */
bool is_lu_power_on_wp;
};
#endif /* End of Header */ #endif /* End of Header */
...@@ -43,34 +43,24 @@ ...@@ -43,34 +43,24 @@
* @pdev: pointer to PCI device handle * @pdev: pointer to PCI device handle
* @state: power state * @state: power state
* *
* Returns -ENOSYS * Returns 0 if successful
* Returns non-zero otherwise
*/ */
static int ufshcd_pci_suspend(struct device *dev) static int ufshcd_pci_suspend(struct device *dev)
{ {
/* return ufshcd_system_suspend(dev_get_drvdata(dev));
* TODO:
* 1. Call ufshcd_suspend
* 2. Do bus specific power management
*/
return -ENOSYS;
} }
/** /**
* ufshcd_pci_resume - resume power management function * ufshcd_pci_resume - resume power management function
* @pdev: pointer to PCI device handle * @pdev: pointer to PCI device handle
* *
* Returns -ENOSYS * Returns 0 if successful
* Returns non-zero otherwise
*/ */
static int ufshcd_pci_resume(struct device *dev) static int ufshcd_pci_resume(struct device *dev)
{ {
/* return ufshcd_system_resume(dev_get_drvdata(dev));
* TODO:
* 1. Call ufshcd_resume.
* 2. Do bus specific wake up
*/
return -ENOSYS;
} }
#else #else
#define ufshcd_pci_suspend NULL #define ufshcd_pci_suspend NULL
...@@ -80,30 +70,15 @@ static int ufshcd_pci_resume(struct device *dev) ...@@ -80,30 +70,15 @@ static int ufshcd_pci_resume(struct device *dev)
#ifdef CONFIG_PM_RUNTIME #ifdef CONFIG_PM_RUNTIME
static int ufshcd_pci_runtime_suspend(struct device *dev) static int ufshcd_pci_runtime_suspend(struct device *dev)
{ {
struct ufs_hba *hba = dev_get_drvdata(dev); return ufshcd_runtime_suspend(dev_get_drvdata(dev));
if (!hba)
return 0;
return ufshcd_runtime_suspend(hba);
} }
static int ufshcd_pci_runtime_resume(struct device *dev) static int ufshcd_pci_runtime_resume(struct device *dev)
{ {
struct ufs_hba *hba = dev_get_drvdata(dev); return ufshcd_runtime_resume(dev_get_drvdata(dev));
if (!hba)
return 0;
return ufshcd_runtime_resume(hba);
} }
static int ufshcd_pci_runtime_idle(struct device *dev) static int ufshcd_pci_runtime_idle(struct device *dev)
{ {
struct ufs_hba *hba = dev_get_drvdata(dev); return ufshcd_runtime_idle(dev_get_drvdata(dev));
if (!hba)
return 0;
return ufshcd_runtime_idle(hba);
} }
#else /* !CONFIG_PM_RUNTIME */ #else /* !CONFIG_PM_RUNTIME */
#define ufshcd_pci_runtime_suspend NULL #define ufshcd_pci_runtime_suspend NULL
...@@ -117,7 +92,7 @@ static int ufshcd_pci_runtime_idle(struct device *dev) ...@@ -117,7 +92,7 @@ static int ufshcd_pci_runtime_idle(struct device *dev)
*/ */
static void ufshcd_pci_shutdown(struct pci_dev *pdev) static void ufshcd_pci_shutdown(struct pci_dev *pdev)
{ {
ufshcd_hba_stop((struct ufs_hba *)pci_get_drvdata(pdev)); ufshcd_shutdown((struct ufs_hba *)pci_get_drvdata(pdev));
} }
/** /**
......
...@@ -225,45 +225,24 @@ static int ufshcd_parse_regulator_info(struct ufs_hba *hba) ...@@ -225,45 +225,24 @@ static int ufshcd_parse_regulator_info(struct ufs_hba *hba)
* ufshcd_pltfrm_suspend - suspend power management function * ufshcd_pltfrm_suspend - suspend power management function
* @dev: pointer to device handle * @dev: pointer to device handle
* *
* * Returns 0 if successful
* Returns 0 * Returns non-zero otherwise
*/ */
static int ufshcd_pltfrm_suspend(struct device *dev) static int ufshcd_pltfrm_suspend(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); return ufshcd_system_suspend(dev_get_drvdata(dev));
struct ufs_hba *hba = platform_get_drvdata(pdev);
/*
* TODO:
* 1. Call ufshcd_suspend
* 2. Do bus specific power management
*/
disable_irq(hba->irq);
return 0;
} }
/** /**
* ufshcd_pltfrm_resume - resume power management function * ufshcd_pltfrm_resume - resume power management function
* @dev: pointer to device handle * @dev: pointer to device handle
* *
* Returns 0 * Returns 0 if successful
* Returns non-zero otherwise
*/ */
static int ufshcd_pltfrm_resume(struct device *dev) static int ufshcd_pltfrm_resume(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); return ufshcd_system_resume(dev_get_drvdata(dev));
struct ufs_hba *hba = platform_get_drvdata(pdev);
/*
* TODO:
* 1. Call ufshcd_resume.
* 2. Do bus specific wake up
*/
enable_irq(hba->irq);
return 0;
} }
#else #else
#define ufshcd_pltfrm_suspend NULL #define ufshcd_pltfrm_suspend NULL
...@@ -273,30 +252,15 @@ static int ufshcd_pltfrm_resume(struct device *dev) ...@@ -273,30 +252,15 @@ static int ufshcd_pltfrm_resume(struct device *dev)
#ifdef CONFIG_PM_RUNTIME #ifdef CONFIG_PM_RUNTIME
static int ufshcd_pltfrm_runtime_suspend(struct device *dev) static int ufshcd_pltfrm_runtime_suspend(struct device *dev)
{ {
struct ufs_hba *hba = dev_get_drvdata(dev); return ufshcd_runtime_suspend(dev_get_drvdata(dev));
if (!hba)
return 0;
return ufshcd_runtime_suspend(hba);
} }
static int ufshcd_pltfrm_runtime_resume(struct device *dev) static int ufshcd_pltfrm_runtime_resume(struct device *dev)
{ {
struct ufs_hba *hba = dev_get_drvdata(dev); return ufshcd_runtime_resume(dev_get_drvdata(dev));
if (!hba)
return 0;
return ufshcd_runtime_resume(hba);
} }
static int ufshcd_pltfrm_runtime_idle(struct device *dev) static int ufshcd_pltfrm_runtime_idle(struct device *dev)
{ {
struct ufs_hba *hba = dev_get_drvdata(dev); return ufshcd_runtime_idle(dev_get_drvdata(dev));
if (!hba)
return 0;
return ufshcd_runtime_idle(hba);
} }
#else /* !CONFIG_PM_RUNTIME */ #else /* !CONFIG_PM_RUNTIME */
#define ufshcd_pltfrm_runtime_suspend NULL #define ufshcd_pltfrm_runtime_suspend NULL
...@@ -304,6 +268,11 @@ static int ufshcd_pltfrm_runtime_idle(struct device *dev) ...@@ -304,6 +268,11 @@ static int ufshcd_pltfrm_runtime_idle(struct device *dev)
#define ufshcd_pltfrm_runtime_idle NULL #define ufshcd_pltfrm_runtime_idle NULL
#endif /* CONFIG_PM_RUNTIME */ #endif /* CONFIG_PM_RUNTIME */
static void ufshcd_pltfrm_shutdown(struct platform_device *pdev)
{
ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev));
}
/** /**
* ufshcd_pltfrm_probe - probe routine of the driver * ufshcd_pltfrm_probe - probe routine of the driver
* @pdev: pointer to Platform device handle * @pdev: pointer to Platform device handle
...@@ -404,6 +373,7 @@ static const struct dev_pm_ops ufshcd_dev_pm_ops = { ...@@ -404,6 +373,7 @@ static const struct dev_pm_ops ufshcd_dev_pm_ops = {
static struct platform_driver ufshcd_pltfrm_driver = { static struct platform_driver ufshcd_pltfrm_driver = {
.probe = ufshcd_pltfrm_probe, .probe = ufshcd_pltfrm_probe,
.remove = ufshcd_pltfrm_remove, .remove = ufshcd_pltfrm_remove,
.shutdown = ufshcd_pltfrm_shutdown,
.driver = { .driver = {
.name = "ufshcd", .name = "ufshcd",
.owner = THIS_MODULE, .owner = THIS_MODULE,
......
This diff is collapsed.
...@@ -96,6 +96,54 @@ struct uic_command { ...@@ -96,6 +96,54 @@ struct uic_command {
struct completion done; struct completion done;
}; };
/* Used to differentiate the power management options */
enum ufs_pm_op {
UFS_RUNTIME_PM,
UFS_SYSTEM_PM,
UFS_SHUTDOWN_PM,
};
#define ufshcd_is_runtime_pm(op) ((op) == UFS_RUNTIME_PM)
#define ufshcd_is_system_pm(op) ((op) == UFS_SYSTEM_PM)
#define ufshcd_is_shutdown_pm(op) ((op) == UFS_SHUTDOWN_PM)
/* Host <-> Device UniPro Link state */
enum uic_link_state {
UIC_LINK_OFF_STATE = 0, /* Link powered down or disabled */
UIC_LINK_ACTIVE_STATE = 1, /* Link is in Fast/Slow/Sleep state */
UIC_LINK_HIBERN8_STATE = 2, /* Link is in Hibernate state */
};
#define ufshcd_is_link_off(hba) ((hba)->uic_link_state == UIC_LINK_OFF_STATE)
#define ufshcd_is_link_active(hba) ((hba)->uic_link_state == \
UIC_LINK_ACTIVE_STATE)
#define ufshcd_is_link_hibern8(hba) ((hba)->uic_link_state == \
UIC_LINK_HIBERN8_STATE)
#define ufshcd_set_link_off(hba) ((hba)->uic_link_state = UIC_LINK_OFF_STATE)
#define ufshcd_set_link_active(hba) ((hba)->uic_link_state = \
UIC_LINK_ACTIVE_STATE)
#define ufshcd_set_link_hibern8(hba) ((hba)->uic_link_state = \
UIC_LINK_HIBERN8_STATE)
/*
* UFS Power management levels.
* Each level is in increasing order of power savings.
*/
enum ufs_pm_level {
UFS_PM_LVL_0, /* UFS_ACTIVE_PWR_MODE, UIC_LINK_ACTIVE_STATE */
UFS_PM_LVL_1, /* UFS_ACTIVE_PWR_MODE, UIC_LINK_HIBERN8_STATE */
UFS_PM_LVL_2, /* UFS_SLEEP_PWR_MODE, UIC_LINK_ACTIVE_STATE */
UFS_PM_LVL_3, /* UFS_SLEEP_PWR_MODE, UIC_LINK_HIBERN8_STATE */
UFS_PM_LVL_4, /* UFS_POWERDOWN_PWR_MODE, UIC_LINK_HIBERN8_STATE */
UFS_PM_LVL_5, /* UFS_POWERDOWN_PWR_MODE, UIC_LINK_OFF_STATE */
UFS_PM_LVL_MAX
};
struct ufs_pm_lvl_states {
enum ufs_dev_pwr_mode dev_state;
enum uic_link_state link_state;
};
/** /**
* struct ufshcd_lrb - local reference block * struct ufshcd_lrb - local reference block
* @utr_descriptor_ptr: UTRD address of the command * @utr_descriptor_ptr: UTRD address of the command
...@@ -184,6 +232,8 @@ struct ufs_clk_info { ...@@ -184,6 +232,8 @@ struct ufs_clk_info {
* variant specific Uni-Pro initialization. * variant specific Uni-Pro initialization.
* @link_startup_notify: called before and after Link startup is carried out * @link_startup_notify: called before and after Link startup is carried out
* to allow variant specific Uni-Pro initialization. * to allow variant specific Uni-Pro initialization.
* @suspend: called during host controller PM callback
* @resume: called during host controller PM callback
*/ */
struct ufs_hba_variant_ops { struct ufs_hba_variant_ops {
const char *name; const char *name;
...@@ -193,6 +243,8 @@ struct ufs_hba_variant_ops { ...@@ -193,6 +243,8 @@ struct ufs_hba_variant_ops {
int (*setup_regulators)(struct ufs_hba *, bool); int (*setup_regulators)(struct ufs_hba *, bool);
int (*hce_enable_notify)(struct ufs_hba *, bool); int (*hce_enable_notify)(struct ufs_hba *, bool);
int (*link_startup_notify)(struct ufs_hba *, bool); int (*link_startup_notify)(struct ufs_hba *, bool);
int (*suspend)(struct ufs_hba *, enum ufs_pm_op);
int (*resume)(struct ufs_hba *, enum ufs_pm_op);
}; };
/** /**
...@@ -274,6 +326,14 @@ struct ufs_hba { ...@@ -274,6 +326,14 @@ struct ufs_hba {
struct scsi_device *sdev_rpmb; struct scsi_device *sdev_rpmb;
struct scsi_device *sdev_boot; struct scsi_device *sdev_boot;
enum ufs_dev_pwr_mode curr_dev_pwr_mode;
enum uic_link_state uic_link_state;
/* Desired UFS power management level during runtime PM */
enum ufs_pm_level rpm_lvl;
/* Desired UFS power management level during system PM */
enum ufs_pm_level spm_lvl;
int pm_op_in_progress;
struct ufshcd_lrb *lrb; struct ufshcd_lrb *lrb;
unsigned long lrb_in_use; unsigned long lrb_in_use;
...@@ -287,16 +347,17 @@ struct ufs_hba { ...@@ -287,16 +347,17 @@ struct ufs_hba {
struct ufs_hba_variant_ops *vops; struct ufs_hba_variant_ops *vops;
void *priv; void *priv;
unsigned int irq; unsigned int irq;
bool is_irq_enabled;
struct uic_command *active_uic_cmd;
struct mutex uic_cmd_mutex;
wait_queue_head_t tm_wq; wait_queue_head_t tm_wq;
wait_queue_head_t tm_tag_wq; wait_queue_head_t tm_tag_wq;
unsigned long tm_condition; unsigned long tm_condition;
unsigned long tm_slots_in_use; unsigned long tm_slots_in_use;
struct completion *pwr_done; struct uic_command *active_uic_cmd;
struct mutex uic_cmd_mutex;
struct completion *uic_async_done;
u32 ufshcd_state; u32 ufshcd_state;
u32 eh_flags; u32 eh_flags;
...@@ -319,9 +380,13 @@ struct ufs_hba { ...@@ -319,9 +380,13 @@ struct ufs_hba {
/* Device management request data */ /* Device management request data */
struct ufs_dev_cmd dev_cmd; struct ufs_dev_cmd dev_cmd;
/* Keeps information of the UFS device connected to this host */
struct ufs_dev_info dev_info;
bool auto_bkops_enabled; bool auto_bkops_enabled;
struct ufs_vreg_info vreg_info; struct ufs_vreg_info vreg_info;
struct list_head clk_list_head; struct list_head clk_list_head;
bool wlun_dev_clr_ua;
}; };
#define ufshcd_writel(hba, val, reg) \ #define ufshcd_writel(hba, val, reg) \
...@@ -348,11 +413,12 @@ static inline void check_upiu_size(void) ...@@ -348,11 +413,12 @@ static inline void check_upiu_size(void)
GENERAL_UPIU_REQUEST_SIZE + QUERY_DESC_MAX_SIZE); GENERAL_UPIU_REQUEST_SIZE + QUERY_DESC_MAX_SIZE);
} }
extern int ufshcd_suspend(struct ufs_hba *hba, pm_message_t state);
extern int ufshcd_resume(struct ufs_hba *hba);
extern int ufshcd_runtime_suspend(struct ufs_hba *hba); extern int ufshcd_runtime_suspend(struct ufs_hba *hba);
extern int ufshcd_runtime_resume(struct ufs_hba *hba); extern int ufshcd_runtime_resume(struct ufs_hba *hba);
extern int ufshcd_runtime_idle(struct ufs_hba *hba); extern int ufshcd_runtime_idle(struct ufs_hba *hba);
extern int ufshcd_system_suspend(struct ufs_hba *hba);
extern int ufshcd_system_resume(struct ufs_hba *hba);
extern int ufshcd_shutdown(struct ufs_hba *hba);
extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel, extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
u8 attr_set, u32 mib_val, u8 peer); u8 attr_set, u32 mib_val, u8 peer);
extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel, extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
......
...@@ -124,8 +124,11 @@ enum { ...@@ -124,8 +124,11 @@ enum {
#define CONTROLLER_FATAL_ERROR UFS_BIT(16) #define CONTROLLER_FATAL_ERROR UFS_BIT(16)
#define SYSTEM_BUS_FATAL_ERROR UFS_BIT(17) #define SYSTEM_BUS_FATAL_ERROR UFS_BIT(17)
#define UFSHCD_UIC_MASK (UIC_COMMAND_COMPL |\ #define UFSHCD_UIC_PWR_MASK (UIC_HIBERNATE_ENTER |\
UIC_POWER_MODE) UIC_HIBERNATE_EXIT |\
UIC_POWER_MODE)
#define UFSHCD_UIC_MASK (UIC_COMMAND_COMPL | UFSHCD_UIC_PWR_MASK)
#define UFSHCD_ERROR_MASK (UIC_ERROR |\ #define UFSHCD_ERROR_MASK (UIC_ERROR |\
DEVICE_FATAL_ERROR |\ DEVICE_FATAL_ERROR |\
...@@ -210,7 +213,7 @@ enum { ...@@ -210,7 +213,7 @@ enum {
#define UIC_GET_ATTR_ID(v) (((v) >> 16) & 0xFFFF) #define UIC_GET_ATTR_ID(v) (((v) >> 16) & 0xFFFF)
/* UIC Commands */ /* UIC Commands */
enum { enum uic_cmd_dme {
UIC_CMD_DME_GET = 0x01, UIC_CMD_DME_GET = 0x01,
UIC_CMD_DME_SET = 0x02, UIC_CMD_DME_SET = 0x02,
UIC_CMD_DME_PEER_GET = 0x03, UIC_CMD_DME_PEER_GET = 0x03,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment