Hi,
On 10/4/26 21:22, Petr Hodina via B4 Relay wrote:
From: Petr Hodina <[email protected]>
Infrastructure (ufs_quirks.h):
- struct ufs_dev_quirk plus UFS_FIX()/UFS_FIX_REVISION() helpers.
- ufshcd_fixup_dev_quirks() applies the table (and sets NO_PURGE for
devices older than UFS_PURGE_SPEC_VER) from ufs_start(), before power
mode negotiation so link quirks take effect.
- hba gains a dev_quirks field, distinct from the host controller
quirks (hba->quirks).
In patch 7 you set the quirk for the Sony Yoshino/Tama platforms, but
I'm not sure we need to add the quirk for those UFSs on other platforms.
Is this really needed for all platforms ?
Neil>
Signed-off-by: Petr Hodina <[email protected]>
---
drivers/ufs/ufs-uclass.c | 52 +++++++++++++++++++++++++++++
drivers/ufs/ufs.h | 2 ++
drivers/ufs/ufs_quirks.h | 87 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 141 insertions(+)
diff --git a/drivers/ufs/ufs-uclass.c b/drivers/ufs/ufs-uclass.c
index 56118a0ee46..05b95c510b4 100644
--- a/drivers/ufs/ufs-uclass.c
+++ b/drivers/ufs/ufs-uclass.c
@@ -29,6 +29,7 @@
#include <linux/dma-mapping.h>
#include "ufs.h"
+#include "ufs_quirks.h"
#define UFSHCD_ENABLE_INTRS (UTP_TRANSFER_REQ_COMPL |\
UTP_TASK_REQ_COMPL |\
@@ -1866,6 +1867,54 @@ out:
return err;
}
+/*
+ * UFS device quirk table. Entries are matched by manufacturer id, model and
+ * fw revision; UFS_ANY_VENDOR / UFS_ANY_MODEL / UFS_ANY_VER act as wildcards.
+ */
+static const struct ufs_dev_quirk ufs_fixups[] = {
+ /* Sony Yoshino/Tama: block PURGE/UNMAP to avoid bricking */
+ UFS_FIX_REVISION(UFS_VENDOR_SKHYNIX, UFS_MODEL_HYNIX_32GB,
+ UFS_REVISION_HYNIX, UFS_DEVICE_QUIRK_NO_PURGE),
+ UFS_FIX_REVISION(UFS_VENDOR_SKHYNIX, UFS_MODEL_HYNIX_64GB,
+ UFS_REVISION_HYNIX, UFS_DEVICE_QUIRK_NO_PURGE),
+ UFS_FIX_REVISION(UFS_VENDOR_SAMSUNG, UFS_MODEL_SAMSUNG_64GB,
+ UFS_REVISION_SAMSUNG, UFS_DEVICE_QUIRK_NO_PURGE),
+ END_FIX
+};
+
+/**
+ * ufshcd_fixup_dev_quirks - apply device-specific quirks
+ *
+ * Sets hba->dev_quirks based on the UFS spec version and the per-device
+ * fixup table above.
+ */
+static void ufshcd_fixup_dev_quirks(struct ufs_hba *hba,
+ struct ufs_dev_desc *dev_desc)
+{
+ const struct ufs_dev_quirk *f;
+
+ /*
+ * Devices implementing an older UFS spec must not be issued PURGE /
+ * UNMAP, doing so erases the bootloader on affected platforms.
+ */
+ if (dev_desc->wspecversion < UFS_PURGE_SPEC_VER)
+ hba->dev_quirks |= UFS_DEVICE_QUIRK_NO_PURGE;
+
+ for (f = ufs_fixups; f->quirk; f++) {
+ if ((f->wmanufacturerid == dev_desc->wmanufacturerid ||
+ f->wmanufacturerid == UFS_ANY_VENDOR) &&
+ (str_prfx_equal(f->model, dev_desc->model) ||
+ !strcmp(f->model, UFS_ANY_MODEL)) &&
+ (str_prfx_equal(f->revision, dev_desc->revision) ||
+ !strcmp(f->revision, UFS_ANY_VER)))
+ hba->dev_quirks |= f->quirk;
+ }
+
+ if (hba->dev_quirks)
+ dev_dbg(hba->dev, "%s: applied device quirks 0x%08x\n",
+ __func__, hba->dev_quirks);
+}
+
struct ufs_ref_clk {
unsigned long freq_hz;
enum ufs_ref_clk_freq val;
@@ -2175,6 +2224,9 @@ static int ufs_start(struct ufs_hba *hba)
return ret;
}
+ /* Apply device-specific quirks before negotiating the power mode */
+ ufshcd_fixup_dev_quirks(hba, &card);
+
ufshcd_set_dev_ref_clk(hba);
if (ufshcd_get_max_pwr_mode(hba)) {
diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h
index e3368a516e4..d4bcb7a1773 100644
--- a/drivers/ufs/ufs.h
+++ b/drivers/ufs/ufs.h
@@ -707,6 +707,8 @@ struct ufs_hba {
u32 version;
u32 intr_mask;
enum ufshcd_quirks quirks;
+ /* UFS device (not host controller) quirks, see ufs_quirks.h */
+ u32 dev_quirks;
/* Virtual memory reference */
struct utp_transfer_cmd_desc *ucdl;
diff --git a/drivers/ufs/ufs_quirks.h b/drivers/ufs/ufs_quirks.h
new file mode 100644
index 00000000000..f5aa47722db
--- /dev/null
+++ b/drivers/ufs/ufs_quirks.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * UFS device quirks
+ *
+ * Ported from the Linux kernel (include/ufs/ufs_quirks.h) and the
+ * Sony Yoshino/Tama downstream UFS quirks/hacks.
+ */
+#ifndef _UFS_QUIRKS_H
+#define _UFS_QUIRKS_H
+
+#include <linux/bitops.h>
+#include <linux/string.h>
+
+/* true if s1 (the fixup pattern) is a prefix of s2 (the device value) */
+static inline bool str_prfx_equal(const char *s1, const char *s2)
+{
+ return strncmp(s1, s2, strlen(s1)) == 0;
+}
+
+#define UFS_ANY_VENDOR 0xFFFF
+#define UFS_ANY_MODEL "ANY_MODEL"
+#define UFS_ANY_VER "ANY_VER"
+
+#define UFS_VENDOR_TOSHIBA 0x198
+#define UFS_VENDOR_SAMSUNG 0x1CE
+#define UFS_VENDOR_SKHYNIX 0x1AD
+#define UFS_VENDOR_MICRON 0x12C
+#define UFS_VENDOR_WDC 0x145
+
+/* UFS Samsung models */
+#define UFS_MODEL_SAMSUNG_64GB "KLUDG4U1EA-B0C1"
+#define UFS_REVISION_SAMSUNG "0100"
+
+/* UFS SK Hynix models */
+#define UFS_MODEL_HYNIX_32GB "hB8aL1"
+#define UFS_MODEL_HYNIX_64GB "hC8aL1"
+#define UFS_REVISION_HYNIX "D001"
+
+/*
+ * Devices with a UFS spec version older than this must not be issued PURGE /
+ * UNMAP (secure erase) operations: on the Sony Yoshino/Tama platforms doing so
+ * erases the bootloader and permanently bricks the device.
+ */
+#define UFS_PURGE_SPEC_VER 0x210
+
+/**
+ * struct ufs_dev_quirk - ufs device quirk info
+ * @wmanufacturerid: card details (UFS_ANY_VENDOR matches any vendor)
+ * @model: card model (UFS_ANY_MODEL matches any model)
+ * @revision: card fw revision (UFS_ANY_VER matches any revision)
+ * @quirk: device quirk bitmask
+ */
+struct ufs_dev_quirk {
+ u16 wmanufacturerid;
+ const char *model;
+ const char *revision;
+ unsigned int quirk;
+};
+
+/* terminator for the fixup table */
+#define END_FIX { 0, NULL, NULL, 0 }
+
+/* add a quirk for any revision of a specific model */
+#define UFS_FIX(_vendor, _model, _quirk) { \
+ .wmanufacturerid = (_vendor), \
+ .model = (_model), \
+ .revision = (UFS_ANY_VER), \
+ .quirk = (_quirk), \
+}
+
+/* add a quirk for a specific model and fw revision */
+#define UFS_FIX_REVISION(_vendor, _model, _revision, _quirk) { \
+ .wmanufacturerid = (_vendor), \
+ .model = (_model), \
+ .revision = (_revision), \
+ .quirk = (_quirk), \
+}
+
+/*
+ * Some Sony UFS devices must not use the PURGE / UNMAP (secure erase)
+ * operation as it would erase the bootloader and permanently brick the
+ * device. This quirk disables PURGE-related functionality for affected
+ * devices.
+ */
+#define UFS_DEVICE_QUIRK_NO_PURGE BIT(0)
+
+#endif /* _UFS_QUIRKS_H */