diff mbox series

[09/11] mmc: core: Read the SD function extension registers for power management

Message ID 20210504161222.101536-10-ulf.hansson@linaro.org
State New
Headers show
Series Initital support for new power/perf features for SD cards | expand

Commit Message

Ulf Hansson May 4, 2021, 4:12 p.m. UTC
In SD spec v4.x the SD function extension registers were introduced. A
specific function register were added to let the card announce support for
optional features in regards to power management. The features that were
added are "Power Off Notification", "Power Down Mode" and "Power
Sustenance".

As a first step, let's read and parse this register for power management
during the SD card initialization and store the information about the
supported features in the struct mmc_card. In this way, we prepare for
subsequent changes to implement the complete support for the new features.

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

---
 drivers/mmc/core/sd.c    | 178 +++++++++++++++++++++++++++++++++++++++
 include/linux/mmc/card.h |  13 +++
 include/linux/mmc/sd.h   |   3 +
 3 files changed, 194 insertions(+)

-- 
2.25.1

Comments

Linus Walleij May 6, 2021, 1:04 p.m. UTC | #1
On Tue, May 4, 2021 at 6:12 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:

> In SD spec v4.x the SD function extension registers were introduced. A

> specific function register were added to let the card announce support for

> optional features in regards to power management. The features that were

> added are "Power Off Notification", "Power Down Mode" and "Power

> Sustenance".

>

> As a first step, let's read and parse this register for power management

> during the SD card initialization and store the information about the

> supported features in the struct mmc_card. In this way, we prepare for

> subsequent changes to implement the complete support for the new features.

>

> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>


Looks to me like it will work just fine.
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>


Yours,
Linus Walleij
Shawn Lin May 7, 2021, 2:06 a.m. UTC | #2
On 2021/5/5 0:12, Ulf Hansson wrote:
> In SD spec v4.x the SD function extension registers were introduced. A


I have a v4.0 spec and it doesn't state that v4.0 suppports reading
extension registers but just says TBD instead.  So I guess v4.x doesn't
include v4.0 ?

> specific function register were added to let the card announce support for

> optional features in regards to power management. The features that were

> added are "Power Off Notification", "Power Down Mode" and "Power

> Sustenance".

> 

> As a first step, let's read and parse this register for power management

> during the SD card initialization and store the information about the

> supported features in the struct mmc_card. In this way, we prepare for

> subsequent changes to implement the complete support for the new features.

> 

> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

> ---

>   drivers/mmc/core/sd.c    | 178 +++++++++++++++++++++++++++++++++++++++

>   include/linux/mmc/card.h |  13 +++

>   include/linux/mmc/sd.h   |   3 +

>   3 files changed, 194 insertions(+)

> 

> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c

> index de7b5f8df550..cb5e8b2fc32f 100644

> --- a/drivers/mmc/core/sd.c

> +++ b/drivers/mmc/core/sd.c

> @@ -996,6 +996,177 @@ static bool mmc_sd_card_using_v18(struct mmc_card *card)

>   	       (SD_MODE_UHS_SDR50 | SD_MODE_UHS_SDR104 | SD_MODE_UHS_DDR50);

>   }

>   

> +static int sd_read_ext_reg(struct mmc_card *card, u8 fno, u8 page,

> +			   u16 offset, u16 len, u8 *reg_buf)

> +{

> +	u32 cmd_args;

> +

> +	/*

> +	 * Command arguments of CMD48:

> +	 * [31:31] MIO (0 = memory).

> +	 * [30:27] FNO (function number).

> +	 * [26:26] reserved (0).

> +	 * [25:18] page number.

> +	 * [17:9] offset address.

> +	 * [8:0] length (0 = 1 byte, 1ff = 512 bytes).

> +	 */

> +	cmd_args = fno << 27 | page << 18 | offset << 9 | (len -1);

> +

> +	return mmc_send_adtc_data(card, card->host, SD_READ_EXTR_SINGLE,

> +				  cmd_args, reg_buf, 512);

> +}

> +

> +static int sd_parse_ext_reg_power(struct mmc_card *card, u8 fno, u8 page,

> +				  u16 offset)

> +{

> +	int err;

> +	u8 *reg_buf;

> +

> +	reg_buf = kzalloc(512, GFP_KERNEL);

> +	if (!reg_buf)

> +		return -ENOMEM;

> +

> +	/* Read the extension register for power management function. */

> +	err = sd_read_ext_reg(card, fno, page, offset, 512, reg_buf);

> +	if (err) {

> +		pr_warn("%s: error %d reading PM func of ext reg\n",

> +			mmc_hostname(card->host), err);

> +		goto out;

> +	}

> +

> +	/* PM revision consists of 4 bits. */

> +	card->ext_power.rev = reg_buf[0] & 0xf;

> +

> +	/* Power Off Notification support at bit 4. */

> +	if (reg_buf[1] & 0x10)

> +		card->ext_power.feature_support |= SD_EXT_POWER_OFF_NOTIFY;

> +

> +	/* Power Sustenance support at bit 5. */

> +	if (reg_buf[1] & 0x20)

> +		card->ext_power.feature_support |= SD_EXT_POWER_SUSTENANCE;

> +

> +	/* Power Down Mode support at bit 6. */

> +	if (reg_buf[1] & 0x40)

> +		card->ext_power.feature_support |= SD_EXT_POWER_DOWN_MODE;

> +

> +	card->ext_power.fno = fno;

> +	card->ext_power.page = page;

> +	card->ext_power.offset = offset;

> +

> +out:

> +	kfree(reg_buf);

> +	return err;

> +}

> +

> +static int sd_parse_ext_reg(struct mmc_card *card, u8 *gen_info_buf,

> +			    u16 *next_ext_addr)

> +{

> +	u8 num_regs, fno, page;

> +	u16 sfc, offset, ext = *next_ext_addr;

> +	u32 reg_addr;

> +

> +	/*

> +	 * Parse only one register set per extension, as that is sufficient to

> +	 * support the standard functions. This means another 48 bytes in the

> +	 * buffer must be available.

> +	 */

> +	if (ext + 48 > 512)

> +		return -EFAULT;

> +

> +	/* Standard Function Code */

> +	memcpy(&sfc, &gen_info_buf[ext], 2);

> +

> +	/* Address to the next extension. */

> +	memcpy(next_ext_addr, &gen_info_buf[ext + 40], 2);

> +

> +	/* Number of registers for this extension. */

> +	num_regs = gen_info_buf[ext + 42];

> +

> +	/* We support only one register per extension. */

> +	if (num_regs != 1)

> +		return 0;

> +

> +	/* Extension register address. */

> +	memcpy(&reg_addr, &gen_info_buf[ext + 44], 4);

> +

> +	/* 9 bits (0 to 8) contains the offset address. */

> +	offset = reg_addr & 0x1ff;

> +

> +	/* 8 bits (9 to 16) contains the page number. */

> +	page = reg_addr >> 9 & 0xff ;

> +

> +	/* 4 bits (18 to 21) contains the function number. */

> +	fno = reg_addr >> 18 & 0xf;

> +

> +	/* Standard Function Code for power management. */

> +	if (sfc == 0x1)

> +		return sd_parse_ext_reg_power(card, fno, page, offset);

> +

> +	return 0;

> +}

> +

> +static int sd_read_ext_regs(struct mmc_card *card)

> +{

> +	int err, i;

> +	u8 num_ext, *gen_info_buf;

> +	u16 rev, len, next_ext_addr;

> +

> +	if (mmc_host_is_spi(card->host))

> +		return 0;

> +

> +	if (!(card->scr.cmds & SD_SCR_CMD48_SUPPORT))

> +		return 0;

> +

> +	gen_info_buf = kzalloc(512, GFP_KERNEL);

> +	if (!gen_info_buf)

> +		return -ENOMEM;

> +

> +	/*

> +	 * Read 512 bytes of general info, which is found at function number 0,

> +	 * at page 0 and with no offset.

> +	 */

> +	err = sd_read_ext_reg(card, 0, 0, 0, 512, gen_info_buf);

> +	if (err) {

> +		pr_warn("%s: error %d reading general info of SD ext reg\n",

> +			mmc_hostname(card->host), err);

> +		goto out;

> +	}

> +

> +	/* General info structure revision. */

> +	memcpy(&rev, &gen_info_buf[0], 2);

> +

> +	/* Length of general info in bytes. */

> +	memcpy(&len, &gen_info_buf[2], 2);

> +

> +	/* Number of extensions to be find. */

> +	num_ext = gen_info_buf[4];

> +

> +	/* We support revision 0, but limit it to 512 bytes for simplicity. */

> +	if (rev != 0 || len > 512) {

> +		pr_warn("%s: non-supported SD ext reg layout\n",

> +			mmc_hostname(card->host));

> +		goto out;

> +	}

> +

> +	/*

> +	 * Parse the extension registers. The first extension should start

> +	 * immediately after the general info header (16 bytes).

> +	 */

> +	next_ext_addr = 16;

> +	for (i = 0; i < num_ext; i++) {

> +		err = sd_parse_ext_reg(card, gen_info_buf, &next_ext_addr);

> +		if (err) {

> +			pr_warn("%s: error %d parsing SD ext reg\n",

> +				mmc_hostname(card->host), err);

> +			goto out;

> +		}

> +	}

> +

> +out:

> +	kfree(gen_info_buf);

> +	return err;

> +}

> +

>   /*

>    * Handle the detection and initialisation of a card.

>    *

> @@ -1144,6 +1315,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,

>   		}

>   	}

>   

> +	if (!oldcard) {

> +		/* Read/parse the extension registers. */

> +		err = sd_read_ext_regs(card);

> +		if (err)

> +			goto free_card;

> +	}

> +

>   	if (host->cqe_ops && !host->cqe_enabled) {

>   		err = host->cqe_ops->cqe_enable(host, card);

>   		if (!err) {

> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h

> index 858fc4d11240..03a862e93594 100644

> --- a/include/linux/mmc/card.h

> +++ b/include/linux/mmc/card.h

> @@ -191,6 +191,18 @@ struct sd_switch_caps {

>   #define SD_MAX_CURRENT_800	(1 << SD_SET_CURRENT_LIMIT_800)

>   };

>   

> +struct sd_ext_reg {

> +	u8			fno;

> +	u8			page;

> +	u16			offset;

> +	u8			rev;

> +	u8			feature_support;

> +/* Power Management Function. */

> +#define SD_EXT_POWER_OFF_NOTIFY	(1<<0)

> +#define SD_EXT_POWER_SUSTENANCE	(1<<1)

> +#define SD_EXT_POWER_DOWN_MODE	(1<<2)

> +};

> +

>   struct sdio_cccr {

>   	unsigned int		sdio_vsn;

>   	unsigned int		sd_vsn;

> @@ -292,6 +304,7 @@ struct mmc_card {

>   	struct sd_scr		scr;		/* extra SD information */

>   	struct sd_ssr		ssr;		/* yet more SD information */

>   	struct sd_switch_caps	sw_caps;	/* switch (CMD6) caps */

> +	struct sd_ext_reg	ext_power;	/* SD extension reg for PM */

>   

>   	unsigned int		sdio_funcs;	/* number of SDIO functions */

>   	atomic_t		sdio_funcs_probed; /* number of probed SDIO funcs */

> diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h

> index 2236aa540faa..43bfc5c39ad4 100644

> --- a/include/linux/mmc/sd.h

> +++ b/include/linux/mmc/sd.h

> @@ -29,6 +29,9 @@

>   #define SD_APP_OP_COND           41   /* bcr  [31:0] OCR         R3  */

>   #define SD_APP_SEND_SCR          51   /* adtc                    R1  */

>   

> +  /* class 11 */

> +#define SD_READ_EXTR_SINGLE      48   /* adtc [31:0]             R1  */

> +

>   /* OCR bit definitions */

>   #define SD_OCR_S18R		(1 << 24)    /* 1.8V switching request */

>   #define SD_ROCR_S18A		SD_OCR_S18R  /* 1.8V switching accepted by card */

>
Ulf Hansson May 7, 2021, 7:27 a.m. UTC | #3
On Fri, 7 May 2021 at 04:06, Shawn Lin <shawn.lin@rock-chips.com> wrote:
>

>

> On 2021/5/5 0:12, Ulf Hansson wrote:

> > In SD spec v4.x the SD function extension registers were introduced. A

>

> I have a v4.0 spec and it doesn't state that v4.0 suppports reading

> extension registers but just says TBD instead.  So I guess v4.x doesn't

> include v4.0 ?


Good question. The v4.0 spec introduces the CMD48/49 and CMD58/59,
while in v4.10 the spec adds the power management extensions.

I can update the commit message to better reflect this, if you prefer!?

Thanks a lot for reviewing!

Kind regards
Uffe

>

> > specific function register were added to let the card announce support for

> > optional features in regards to power management. The features that were

> > added are "Power Off Notification", "Power Down Mode" and "Power

> > Sustenance".

> >

> > As a first step, let's read and parse this register for power management

> > during the SD card initialization and store the information about the

> > supported features in the struct mmc_card. In this way, we prepare for

> > subsequent changes to implement the complete support for the new features.

> >

> > Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

> > ---

> >   drivers/mmc/core/sd.c    | 178 +++++++++++++++++++++++++++++++++++++++

> >   include/linux/mmc/card.h |  13 +++

> >   include/linux/mmc/sd.h   |   3 +

> >   3 files changed, 194 insertions(+)

> >

> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c

> > index de7b5f8df550..cb5e8b2fc32f 100644

> > --- a/drivers/mmc/core/sd.c

> > +++ b/drivers/mmc/core/sd.c

> > @@ -996,6 +996,177 @@ static bool mmc_sd_card_using_v18(struct mmc_card *card)

> >              (SD_MODE_UHS_SDR50 | SD_MODE_UHS_SDR104 | SD_MODE_UHS_DDR50);

> >   }

> >

> > +static int sd_read_ext_reg(struct mmc_card *card, u8 fno, u8 page,

> > +                        u16 offset, u16 len, u8 *reg_buf)

> > +{

> > +     u32 cmd_args;

> > +

> > +     /*

> > +      * Command arguments of CMD48:

> > +      * [31:31] MIO (0 = memory).

> > +      * [30:27] FNO (function number).

> > +      * [26:26] reserved (0).

> > +      * [25:18] page number.

> > +      * [17:9] offset address.

> > +      * [8:0] length (0 = 1 byte, 1ff = 512 bytes).

> > +      */

> > +     cmd_args = fno << 27 | page << 18 | offset << 9 | (len -1);

> > +

> > +     return mmc_send_adtc_data(card, card->host, SD_READ_EXTR_SINGLE,

> > +                               cmd_args, reg_buf, 512);

> > +}

> > +

> > +static int sd_parse_ext_reg_power(struct mmc_card *card, u8 fno, u8 page,

> > +                               u16 offset)

> > +{

> > +     int err;

> > +     u8 *reg_buf;

> > +

> > +     reg_buf = kzalloc(512, GFP_KERNEL);

> > +     if (!reg_buf)

> > +             return -ENOMEM;

> > +

> > +     /* Read the extension register for power management function. */

> > +     err = sd_read_ext_reg(card, fno, page, offset, 512, reg_buf);

> > +     if (err) {

> > +             pr_warn("%s: error %d reading PM func of ext reg\n",

> > +                     mmc_hostname(card->host), err);

> > +             goto out;

> > +     }

> > +

> > +     /* PM revision consists of 4 bits. */

> > +     card->ext_power.rev = reg_buf[0] & 0xf;

> > +

> > +     /* Power Off Notification support at bit 4. */

> > +     if (reg_buf[1] & 0x10)

> > +             card->ext_power.feature_support |= SD_EXT_POWER_OFF_NOTIFY;

> > +

> > +     /* Power Sustenance support at bit 5. */

> > +     if (reg_buf[1] & 0x20)

> > +             card->ext_power.feature_support |= SD_EXT_POWER_SUSTENANCE;

> > +

> > +     /* Power Down Mode support at bit 6. */

> > +     if (reg_buf[1] & 0x40)

> > +             card->ext_power.feature_support |= SD_EXT_POWER_DOWN_MODE;

> > +

> > +     card->ext_power.fno = fno;

> > +     card->ext_power.page = page;

> > +     card->ext_power.offset = offset;

> > +

> > +out:

> > +     kfree(reg_buf);

> > +     return err;

> > +}

> > +

> > +static int sd_parse_ext_reg(struct mmc_card *card, u8 *gen_info_buf,

> > +                         u16 *next_ext_addr)

> > +{

> > +     u8 num_regs, fno, page;

> > +     u16 sfc, offset, ext = *next_ext_addr;

> > +     u32 reg_addr;

> > +

> > +     /*

> > +      * Parse only one register set per extension, as that is sufficient to

> > +      * support the standard functions. This means another 48 bytes in the

> > +      * buffer must be available.

> > +      */

> > +     if (ext + 48 > 512)

> > +             return -EFAULT;

> > +

> > +     /* Standard Function Code */

> > +     memcpy(&sfc, &gen_info_buf[ext], 2);

> > +

> > +     /* Address to the next extension. */

> > +     memcpy(next_ext_addr, &gen_info_buf[ext + 40], 2);

> > +

> > +     /* Number of registers for this extension. */

> > +     num_regs = gen_info_buf[ext + 42];

> > +

> > +     /* We support only one register per extension. */

> > +     if (num_regs != 1)

> > +             return 0;

> > +

> > +     /* Extension register address. */

> > +     memcpy(&reg_addr, &gen_info_buf[ext + 44], 4);

> > +

> > +     /* 9 bits (0 to 8) contains the offset address. */

> > +     offset = reg_addr & 0x1ff;

> > +

> > +     /* 8 bits (9 to 16) contains the page number. */

> > +     page = reg_addr >> 9 & 0xff ;

> > +

> > +     /* 4 bits (18 to 21) contains the function number. */

> > +     fno = reg_addr >> 18 & 0xf;

> > +

> > +     /* Standard Function Code for power management. */

> > +     if (sfc == 0x1)

> > +             return sd_parse_ext_reg_power(card, fno, page, offset);

> > +

> > +     return 0;

> > +}

> > +

> > +static int sd_read_ext_regs(struct mmc_card *card)

> > +{

> > +     int err, i;

> > +     u8 num_ext, *gen_info_buf;

> > +     u16 rev, len, next_ext_addr;

> > +

> > +     if (mmc_host_is_spi(card->host))

> > +             return 0;

> > +

> > +     if (!(card->scr.cmds & SD_SCR_CMD48_SUPPORT))

> > +             return 0;

> > +

> > +     gen_info_buf = kzalloc(512, GFP_KERNEL);

> > +     if (!gen_info_buf)

> > +             return -ENOMEM;

> > +

> > +     /*

> > +      * Read 512 bytes of general info, which is found at function number 0,

> > +      * at page 0 and with no offset.

> > +      */

> > +     err = sd_read_ext_reg(card, 0, 0, 0, 512, gen_info_buf);

> > +     if (err) {

> > +             pr_warn("%s: error %d reading general info of SD ext reg\n",

> > +                     mmc_hostname(card->host), err);

> > +             goto out;

> > +     }

> > +

> > +     /* General info structure revision. */

> > +     memcpy(&rev, &gen_info_buf[0], 2);

> > +

> > +     /* Length of general info in bytes. */

> > +     memcpy(&len, &gen_info_buf[2], 2);

> > +

> > +     /* Number of extensions to be find. */

> > +     num_ext = gen_info_buf[4];

> > +

> > +     /* We support revision 0, but limit it to 512 bytes for simplicity. */

> > +     if (rev != 0 || len > 512) {

> > +             pr_warn("%s: non-supported SD ext reg layout\n",

> > +                     mmc_hostname(card->host));

> > +             goto out;

> > +     }

> > +

> > +     /*

> > +      * Parse the extension registers. The first extension should start

> > +      * immediately after the general info header (16 bytes).

> > +      */

> > +     next_ext_addr = 16;

> > +     for (i = 0; i < num_ext; i++) {

> > +             err = sd_parse_ext_reg(card, gen_info_buf, &next_ext_addr);

> > +             if (err) {

> > +                     pr_warn("%s: error %d parsing SD ext reg\n",

> > +                             mmc_hostname(card->host), err);

> > +                     goto out;

> > +             }

> > +     }

> > +

> > +out:

> > +     kfree(gen_info_buf);

> > +     return err;

> > +}

> > +

> >   /*

> >    * Handle the detection and initialisation of a card.

> >    *

> > @@ -1144,6 +1315,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,

> >               }

> >       }

> >

> > +     if (!oldcard) {

> > +             /* Read/parse the extension registers. */

> > +             err = sd_read_ext_regs(card);

> > +             if (err)

> > +                     goto free_card;

> > +     }

> > +

> >       if (host->cqe_ops && !host->cqe_enabled) {

> >               err = host->cqe_ops->cqe_enable(host, card);

> >               if (!err) {

> > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h

> > index 858fc4d11240..03a862e93594 100644

> > --- a/include/linux/mmc/card.h

> > +++ b/include/linux/mmc/card.h

> > @@ -191,6 +191,18 @@ struct sd_switch_caps {

> >   #define SD_MAX_CURRENT_800  (1 << SD_SET_CURRENT_LIMIT_800)

> >   };

> >

> > +struct sd_ext_reg {

> > +     u8                      fno;

> > +     u8                      page;

> > +     u16                     offset;

> > +     u8                      rev;

> > +     u8                      feature_support;

> > +/* Power Management Function. */

> > +#define SD_EXT_POWER_OFF_NOTIFY      (1<<0)

> > +#define SD_EXT_POWER_SUSTENANCE      (1<<1)

> > +#define SD_EXT_POWER_DOWN_MODE       (1<<2)

> > +};

> > +

> >   struct sdio_cccr {

> >       unsigned int            sdio_vsn;

> >       unsigned int            sd_vsn;

> > @@ -292,6 +304,7 @@ struct mmc_card {

> >       struct sd_scr           scr;            /* extra SD information */

> >       struct sd_ssr           ssr;            /* yet more SD information */

> >       struct sd_switch_caps   sw_caps;        /* switch (CMD6) caps */

> > +     struct sd_ext_reg       ext_power;      /* SD extension reg for PM */

> >

> >       unsigned int            sdio_funcs;     /* number of SDIO functions */

> >       atomic_t                sdio_funcs_probed; /* number of probed SDIO funcs */

> > diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h

> > index 2236aa540faa..43bfc5c39ad4 100644

> > --- a/include/linux/mmc/sd.h

> > +++ b/include/linux/mmc/sd.h

> > @@ -29,6 +29,9 @@

> >   #define SD_APP_OP_COND           41   /* bcr  [31:0] OCR         R3  */

> >   #define SD_APP_SEND_SCR          51   /* adtc                    R1  */

> >

> > +  /* class 11 */

> > +#define SD_READ_EXTR_SINGLE      48   /* adtc [31:0]             R1  */

> > +

> >   /* OCR bit definitions */

> >   #define SD_OCR_S18R         (1 << 24)    /* 1.8V switching request */

> >   #define SD_ROCR_S18A                SD_OCR_S18R  /* 1.8V switching accepted by card */

> >

>

>
Shawn Lin May 7, 2021, 7:48 a.m. UTC | #4
On 2021/5/7 15:27, Ulf Hansson wrote:
> On Fri, 7 May 2021 at 04:06, Shawn Lin <shawn.lin@rock-chips.com> wrote:

>>

>>

>> On 2021/5/5 0:12, Ulf Hansson wrote:

>>> In SD spec v4.x the SD function extension registers were introduced. A

>>

>> I have a v4.0 spec and it doesn't state that v4.0 suppports reading

>> extension registers but just says TBD instead.  So I guess v4.x doesn't

>> include v4.0 ?

> 

> Good question. The v4.0 spec introduces the CMD48/49 and CMD58/59,

> while in v4.10 the spec adds the power management extensions.

> 

> I can update the commit message to better reflect this, if you prefer!?


It would be better.

And I downloaded the latest v8.00 spec, checked carefully with the new
features there to make sure we don't make any misinterpretations at
first.

For patch 9 -11 as well,

Reviewed-by: Shawn Lin <shawn.lin@rock-chips.con>


> 

> Thanks a lot for reviewing!

> 

> Kind regards

> Uffe

> 

>>

>>> specific function register were added to let the card announce support for

>>> optional features in regards to power management. The features that were

>>> added are "Power Off Notification", "Power Down Mode" and "Power

>>> Sustenance".

>>>

>>> As a first step, let's read and parse this register for power management

>>> during the SD card initialization and store the information about the

>>> supported features in the struct mmc_card. In this way, we prepare for

>>> subsequent changes to implement the complete support for the new features.

>>>

>>> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

>>> ---

>>>    drivers/mmc/core/sd.c    | 178 +++++++++++++++++++++++++++++++++++++++

>>>    include/linux/mmc/card.h |  13 +++

>>>    include/linux/mmc/sd.h   |   3 +

>>>    3 files changed, 194 insertions(+)

>>>

>>> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c

>>> index de7b5f8df550..cb5e8b2fc32f 100644

>>> --- a/drivers/mmc/core/sd.c

>>> +++ b/drivers/mmc/core/sd.c

>>> @@ -996,6 +996,177 @@ static bool mmc_sd_card_using_v18(struct mmc_card *card)

>>>               (SD_MODE_UHS_SDR50 | SD_MODE_UHS_SDR104 | SD_MODE_UHS_DDR50);

>>>    }

>>>

>>> +static int sd_read_ext_reg(struct mmc_card *card, u8 fno, u8 page,

>>> +                        u16 offset, u16 len, u8 *reg_buf)

>>> +{

>>> +     u32 cmd_args;

>>> +

>>> +     /*

>>> +      * Command arguments of CMD48:

>>> +      * [31:31] MIO (0 = memory).

>>> +      * [30:27] FNO (function number).

>>> +      * [26:26] reserved (0).

>>> +      * [25:18] page number.

>>> +      * [17:9] offset address.

>>> +      * [8:0] length (0 = 1 byte, 1ff = 512 bytes).

>>> +      */

>>> +     cmd_args = fno << 27 | page << 18 | offset << 9 | (len -1);

>>> +

>>> +     return mmc_send_adtc_data(card, card->host, SD_READ_EXTR_SINGLE,

>>> +                               cmd_args, reg_buf, 512);

>>> +}

>>> +

>>> +static int sd_parse_ext_reg_power(struct mmc_card *card, u8 fno, u8 page,

>>> +                               u16 offset)

>>> +{

>>> +     int err;

>>> +     u8 *reg_buf;

>>> +

>>> +     reg_buf = kzalloc(512, GFP_KERNEL);

>>> +     if (!reg_buf)

>>> +             return -ENOMEM;

>>> +

>>> +     /* Read the extension register for power management function. */

>>> +     err = sd_read_ext_reg(card, fno, page, offset, 512, reg_buf);

>>> +     if (err) {

>>> +             pr_warn("%s: error %d reading PM func of ext reg\n",

>>> +                     mmc_hostname(card->host), err);

>>> +             goto out;

>>> +     }

>>> +

>>> +     /* PM revision consists of 4 bits. */

>>> +     card->ext_power.rev = reg_buf[0] & 0xf;

>>> +

>>> +     /* Power Off Notification support at bit 4. */

>>> +     if (reg_buf[1] & 0x10)

>>> +             card->ext_power.feature_support |= SD_EXT_POWER_OFF_NOTIFY;

>>> +

>>> +     /* Power Sustenance support at bit 5. */

>>> +     if (reg_buf[1] & 0x20)

>>> +             card->ext_power.feature_support |= SD_EXT_POWER_SUSTENANCE;

>>> +

>>> +     /* Power Down Mode support at bit 6. */

>>> +     if (reg_buf[1] & 0x40)

>>> +             card->ext_power.feature_support |= SD_EXT_POWER_DOWN_MODE;

>>> +

>>> +     card->ext_power.fno = fno;

>>> +     card->ext_power.page = page;

>>> +     card->ext_power.offset = offset;

>>> +

>>> +out:

>>> +     kfree(reg_buf);

>>> +     return err;

>>> +}

>>> +

>>> +static int sd_parse_ext_reg(struct mmc_card *card, u8 *gen_info_buf,

>>> +                         u16 *next_ext_addr)

>>> +{

>>> +     u8 num_regs, fno, page;

>>> +     u16 sfc, offset, ext = *next_ext_addr;

>>> +     u32 reg_addr;

>>> +

>>> +     /*

>>> +      * Parse only one register set per extension, as that is sufficient to

>>> +      * support the standard functions. This means another 48 bytes in the

>>> +      * buffer must be available.

>>> +      */

>>> +     if (ext + 48 > 512)

>>> +             return -EFAULT;

>>> +

>>> +     /* Standard Function Code */

>>> +     memcpy(&sfc, &gen_info_buf[ext], 2);

>>> +

>>> +     /* Address to the next extension. */

>>> +     memcpy(next_ext_addr, &gen_info_buf[ext + 40], 2);

>>> +

>>> +     /* Number of registers for this extension. */

>>> +     num_regs = gen_info_buf[ext + 42];

>>> +

>>> +     /* We support only one register per extension. */

>>> +     if (num_regs != 1)

>>> +             return 0;

>>> +

>>> +     /* Extension register address. */

>>> +     memcpy(&reg_addr, &gen_info_buf[ext + 44], 4);

>>> +

>>> +     /* 9 bits (0 to 8) contains the offset address. */

>>> +     offset = reg_addr & 0x1ff;

>>> +

>>> +     /* 8 bits (9 to 16) contains the page number. */

>>> +     page = reg_addr >> 9 & 0xff ;

>>> +

>>> +     /* 4 bits (18 to 21) contains the function number. */

>>> +     fno = reg_addr >> 18 & 0xf;

>>> +

>>> +     /* Standard Function Code for power management. */

>>> +     if (sfc == 0x1)

>>> +             return sd_parse_ext_reg_power(card, fno, page, offset);

>>> +

>>> +     return 0;

>>> +}

>>> +

>>> +static int sd_read_ext_regs(struct mmc_card *card)

>>> +{

>>> +     int err, i;

>>> +     u8 num_ext, *gen_info_buf;

>>> +     u16 rev, len, next_ext_addr;

>>> +

>>> +     if (mmc_host_is_spi(card->host))

>>> +             return 0;

>>> +

>>> +     if (!(card->scr.cmds & SD_SCR_CMD48_SUPPORT))

>>> +             return 0;

>>> +

>>> +     gen_info_buf = kzalloc(512, GFP_KERNEL);

>>> +     if (!gen_info_buf)

>>> +             return -ENOMEM;

>>> +

>>> +     /*

>>> +      * Read 512 bytes of general info, which is found at function number 0,

>>> +      * at page 0 and with no offset.

>>> +      */

>>> +     err = sd_read_ext_reg(card, 0, 0, 0, 512, gen_info_buf);

>>> +     if (err) {

>>> +             pr_warn("%s: error %d reading general info of SD ext reg\n",

>>> +                     mmc_hostname(card->host), err);

>>> +             goto out;

>>> +     }

>>> +

>>> +     /* General info structure revision. */

>>> +     memcpy(&rev, &gen_info_buf[0], 2);

>>> +

>>> +     /* Length of general info in bytes. */

>>> +     memcpy(&len, &gen_info_buf[2], 2);

>>> +

>>> +     /* Number of extensions to be find. */

>>> +     num_ext = gen_info_buf[4];

>>> +

>>> +     /* We support revision 0, but limit it to 512 bytes for simplicity. */

>>> +     if (rev != 0 || len > 512) {

>>> +             pr_warn("%s: non-supported SD ext reg layout\n",

>>> +                     mmc_hostname(card->host));

>>> +             goto out;

>>> +     }

>>> +

>>> +     /*

>>> +      * Parse the extension registers. The first extension should start

>>> +      * immediately after the general info header (16 bytes).

>>> +      */

>>> +     next_ext_addr = 16;

>>> +     for (i = 0; i < num_ext; i++) {

>>> +             err = sd_parse_ext_reg(card, gen_info_buf, &next_ext_addr);

>>> +             if (err) {

>>> +                     pr_warn("%s: error %d parsing SD ext reg\n",

>>> +                             mmc_hostname(card->host), err);

>>> +                     goto out;

>>> +             }

>>> +     }

>>> +

>>> +out:

>>> +     kfree(gen_info_buf);

>>> +     return err;

>>> +}

>>> +

>>>    /*

>>>     * Handle the detection and initialisation of a card.

>>>     *

>>> @@ -1144,6 +1315,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,

>>>                }

>>>        }

>>>

>>> +     if (!oldcard) {

>>> +             /* Read/parse the extension registers. */

>>> +             err = sd_read_ext_regs(card);

>>> +             if (err)

>>> +                     goto free_card;

>>> +     }

>>> +

>>>        if (host->cqe_ops && !host->cqe_enabled) {

>>>                err = host->cqe_ops->cqe_enable(host, card);

>>>                if (!err) {

>>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h

>>> index 858fc4d11240..03a862e93594 100644

>>> --- a/include/linux/mmc/card.h

>>> +++ b/include/linux/mmc/card.h

>>> @@ -191,6 +191,18 @@ struct sd_switch_caps {

>>>    #define SD_MAX_CURRENT_800  (1 << SD_SET_CURRENT_LIMIT_800)

>>>    };

>>>

>>> +struct sd_ext_reg {

>>> +     u8                      fno;

>>> +     u8                      page;

>>> +     u16                     offset;

>>> +     u8                      rev;

>>> +     u8                      feature_support;

>>> +/* Power Management Function. */

>>> +#define SD_EXT_POWER_OFF_NOTIFY      (1<<0)

>>> +#define SD_EXT_POWER_SUSTENANCE      (1<<1)

>>> +#define SD_EXT_POWER_DOWN_MODE       (1<<2)

>>> +};

>>> +

>>>    struct sdio_cccr {

>>>        unsigned int            sdio_vsn;

>>>        unsigned int            sd_vsn;

>>> @@ -292,6 +304,7 @@ struct mmc_card {

>>>        struct sd_scr           scr;            /* extra SD information */

>>>        struct sd_ssr           ssr;            /* yet more SD information */

>>>        struct sd_switch_caps   sw_caps;        /* switch (CMD6) caps */

>>> +     struct sd_ext_reg       ext_power;      /* SD extension reg for PM */

>>>

>>>        unsigned int            sdio_funcs;     /* number of SDIO functions */

>>>        atomic_t                sdio_funcs_probed; /* number of probed SDIO funcs */

>>> diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h

>>> index 2236aa540faa..43bfc5c39ad4 100644

>>> --- a/include/linux/mmc/sd.h

>>> +++ b/include/linux/mmc/sd.h

>>> @@ -29,6 +29,9 @@

>>>    #define SD_APP_OP_COND           41   /* bcr  [31:0] OCR         R3  */

>>>    #define SD_APP_SEND_SCR          51   /* adtc                    R1  */

>>>

>>> +  /* class 11 */

>>> +#define SD_READ_EXTR_SINGLE      48   /* adtc [31:0]             R1  */

>>> +

>>>    /* OCR bit definitions */

>>>    #define SD_OCR_S18R         (1 << 24)    /* 1.8V switching request */

>>>    #define SD_ROCR_S18A                SD_OCR_S18R  /* 1.8V switching accepted by card */

>>>

>>

>>

> 

> 

>
Ulf Hansson May 7, 2021, 11:36 a.m. UTC | #5
On Fri, 7 May 2021 at 09:48, Shawn Lin <shawn.lin@rock-chips.com> wrote:
>

> On 2021/5/7 15:27, Ulf Hansson wrote:

> > On Fri, 7 May 2021 at 04:06, Shawn Lin <shawn.lin@rock-chips.com> wrote:

> >>

> >>

> >> On 2021/5/5 0:12, Ulf Hansson wrote:

> >>> In SD spec v4.x the SD function extension registers were introduced. A

> >>

> >> I have a v4.0 spec and it doesn't state that v4.0 suppports reading

> >> extension registers but just says TBD instead.  So I guess v4.x doesn't

> >> include v4.0 ?

> >

> > Good question. The v4.0 spec introduces the CMD48/49 and CMD58/59,

> > while in v4.10 the spec adds the power management extensions.

> >

> > I can update the commit message to better reflect this, if you prefer!?

>

> It would be better.


Sure, let me amend the change when applying.

>

> And I downloaded the latest v8.00 spec, checked carefully with the new

> features there to make sure we don't make any misinterpretations at

> first.

>

> For patch 9 -11 as well,

>

> Reviewed-by: Shawn Lin <shawn.lin@rock-chips.con>


Thanks!

[...]

Kind regards
Uffe
diff mbox series

Patch

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index de7b5f8df550..cb5e8b2fc32f 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -996,6 +996,177 @@  static bool mmc_sd_card_using_v18(struct mmc_card *card)
 	       (SD_MODE_UHS_SDR50 | SD_MODE_UHS_SDR104 | SD_MODE_UHS_DDR50);
 }
 
+static int sd_read_ext_reg(struct mmc_card *card, u8 fno, u8 page,
+			   u16 offset, u16 len, u8 *reg_buf)
+{
+	u32 cmd_args;
+
+	/*
+	 * Command arguments of CMD48:
+	 * [31:31] MIO (0 = memory).
+	 * [30:27] FNO (function number).
+	 * [26:26] reserved (0).
+	 * [25:18] page number.
+	 * [17:9] offset address.
+	 * [8:0] length (0 = 1 byte, 1ff = 512 bytes).
+	 */
+	cmd_args = fno << 27 | page << 18 | offset << 9 | (len -1);
+
+	return mmc_send_adtc_data(card, card->host, SD_READ_EXTR_SINGLE,
+				  cmd_args, reg_buf, 512);
+}
+
+static int sd_parse_ext_reg_power(struct mmc_card *card, u8 fno, u8 page,
+				  u16 offset)
+{
+	int err;
+	u8 *reg_buf;
+
+	reg_buf = kzalloc(512, GFP_KERNEL);
+	if (!reg_buf)
+		return -ENOMEM;
+
+	/* Read the extension register for power management function. */
+	err = sd_read_ext_reg(card, fno, page, offset, 512, reg_buf);
+	if (err) {
+		pr_warn("%s: error %d reading PM func of ext reg\n",
+			mmc_hostname(card->host), err);
+		goto out;
+	}
+
+	/* PM revision consists of 4 bits. */
+	card->ext_power.rev = reg_buf[0] & 0xf;
+
+	/* Power Off Notification support at bit 4. */
+	if (reg_buf[1] & 0x10)
+		card->ext_power.feature_support |= SD_EXT_POWER_OFF_NOTIFY;
+
+	/* Power Sustenance support at bit 5. */
+	if (reg_buf[1] & 0x20)
+		card->ext_power.feature_support |= SD_EXT_POWER_SUSTENANCE;
+
+	/* Power Down Mode support at bit 6. */
+	if (reg_buf[1] & 0x40)
+		card->ext_power.feature_support |= SD_EXT_POWER_DOWN_MODE;
+
+	card->ext_power.fno = fno;
+	card->ext_power.page = page;
+	card->ext_power.offset = offset;
+
+out:
+	kfree(reg_buf);
+	return err;
+}
+
+static int sd_parse_ext_reg(struct mmc_card *card, u8 *gen_info_buf,
+			    u16 *next_ext_addr)
+{
+	u8 num_regs, fno, page;
+	u16 sfc, offset, ext = *next_ext_addr;
+	u32 reg_addr;
+
+	/*
+	 * Parse only one register set per extension, as that is sufficient to
+	 * support the standard functions. This means another 48 bytes in the
+	 * buffer must be available.
+	 */
+	if (ext + 48 > 512)
+		return -EFAULT;
+
+	/* Standard Function Code */
+	memcpy(&sfc, &gen_info_buf[ext], 2);
+
+	/* Address to the next extension. */
+	memcpy(next_ext_addr, &gen_info_buf[ext + 40], 2);
+
+	/* Number of registers for this extension. */
+	num_regs = gen_info_buf[ext + 42];
+
+	/* We support only one register per extension. */
+	if (num_regs != 1)
+		return 0;
+
+	/* Extension register address. */
+	memcpy(&reg_addr, &gen_info_buf[ext + 44], 4);
+
+	/* 9 bits (0 to 8) contains the offset address. */
+	offset = reg_addr & 0x1ff;
+
+	/* 8 bits (9 to 16) contains the page number. */
+	page = reg_addr >> 9 & 0xff ;
+
+	/* 4 bits (18 to 21) contains the function number. */
+	fno = reg_addr >> 18 & 0xf;
+
+	/* Standard Function Code for power management. */
+	if (sfc == 0x1)
+		return sd_parse_ext_reg_power(card, fno, page, offset);
+
+	return 0;
+}
+
+static int sd_read_ext_regs(struct mmc_card *card)
+{
+	int err, i;
+	u8 num_ext, *gen_info_buf;
+	u16 rev, len, next_ext_addr;
+
+	if (mmc_host_is_spi(card->host))
+		return 0;
+
+	if (!(card->scr.cmds & SD_SCR_CMD48_SUPPORT))
+		return 0;
+
+	gen_info_buf = kzalloc(512, GFP_KERNEL);
+	if (!gen_info_buf)
+		return -ENOMEM;
+
+	/*
+	 * Read 512 bytes of general info, which is found at function number 0,
+	 * at page 0 and with no offset.
+	 */
+	err = sd_read_ext_reg(card, 0, 0, 0, 512, gen_info_buf);
+	if (err) {
+		pr_warn("%s: error %d reading general info of SD ext reg\n",
+			mmc_hostname(card->host), err);
+		goto out;
+	}
+
+	/* General info structure revision. */
+	memcpy(&rev, &gen_info_buf[0], 2);
+
+	/* Length of general info in bytes. */
+	memcpy(&len, &gen_info_buf[2], 2);
+
+	/* Number of extensions to be find. */
+	num_ext = gen_info_buf[4];
+
+	/* We support revision 0, but limit it to 512 bytes for simplicity. */
+	if (rev != 0 || len > 512) {
+		pr_warn("%s: non-supported SD ext reg layout\n",
+			mmc_hostname(card->host));
+		goto out;
+	}
+
+	/*
+	 * Parse the extension registers. The first extension should start
+	 * immediately after the general info header (16 bytes).
+	 */
+	next_ext_addr = 16;
+	for (i = 0; i < num_ext; i++) {
+		err = sd_parse_ext_reg(card, gen_info_buf, &next_ext_addr);
+		if (err) {
+			pr_warn("%s: error %d parsing SD ext reg\n",
+				mmc_hostname(card->host), err);
+			goto out;
+		}
+	}
+
+out:
+	kfree(gen_info_buf);
+	return err;
+}
+
 /*
  * Handle the detection and initialisation of a card.
  *
@@ -1144,6 +1315,13 @@  static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
 		}
 	}
 
+	if (!oldcard) {
+		/* Read/parse the extension registers. */
+		err = sd_read_ext_regs(card);
+		if (err)
+			goto free_card;
+	}
+
 	if (host->cqe_ops && !host->cqe_enabled) {
 		err = host->cqe_ops->cqe_enable(host, card);
 		if (!err) {
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 858fc4d11240..03a862e93594 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -191,6 +191,18 @@  struct sd_switch_caps {
 #define SD_MAX_CURRENT_800	(1 << SD_SET_CURRENT_LIMIT_800)
 };
 
+struct sd_ext_reg {
+	u8			fno;
+	u8			page;
+	u16			offset;
+	u8			rev;
+	u8			feature_support;
+/* Power Management Function. */
+#define SD_EXT_POWER_OFF_NOTIFY	(1<<0)
+#define SD_EXT_POWER_SUSTENANCE	(1<<1)
+#define SD_EXT_POWER_DOWN_MODE	(1<<2)
+};
+
 struct sdio_cccr {
 	unsigned int		sdio_vsn;
 	unsigned int		sd_vsn;
@@ -292,6 +304,7 @@  struct mmc_card {
 	struct sd_scr		scr;		/* extra SD information */
 	struct sd_ssr		ssr;		/* yet more SD information */
 	struct sd_switch_caps	sw_caps;	/* switch (CMD6) caps */
+	struct sd_ext_reg	ext_power;	/* SD extension reg for PM */
 
 	unsigned int		sdio_funcs;	/* number of SDIO functions */
 	atomic_t		sdio_funcs_probed; /* number of probed SDIO funcs */
diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h
index 2236aa540faa..43bfc5c39ad4 100644
--- a/include/linux/mmc/sd.h
+++ b/include/linux/mmc/sd.h
@@ -29,6 +29,9 @@ 
 #define SD_APP_OP_COND           41   /* bcr  [31:0] OCR         R3  */
 #define SD_APP_SEND_SCR          51   /* adtc                    R1  */
 
+  /* class 11 */
+#define SD_READ_EXTR_SINGLE      48   /* adtc [31:0]             R1  */
+
 /* OCR bit definitions */
 #define SD_OCR_S18R		(1 << 24)    /* 1.8V switching request */
 #define SD_ROCR_S18A		SD_OCR_S18R  /* 1.8V switching accepted by card */