From 515a2f7c021fbe1c3dcf897d7337800f721f87b5 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 9 Feb 2022 22:16:22 +0100 Subject: video: bmp: Support x2r10g10b10 pixel format Fixes the display of the u-boot logo on Apple silicon devices. Signed-off-by: Janne Grunau Reviewed-by: Simon Glass --- drivers/video/video_bmp.c | 70 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 14 deletions(-) diff --git a/drivers/video/video_bmp.c b/drivers/video/video_bmp.c index c8c3fd3549..4d2d961696 100644 --- a/drivers/video/video_bmp.c +++ b/drivers/video/video_bmp.c @@ -30,6 +30,18 @@ static uint get_bmp_col_16bpp(struct bmp_color_table_entry cte) ((cte.blue >> 3) & 0x001f); } +/** + * get_bmp_col_x2r10g10b10() - Convert a colour-table entry into a x2r10g10b10 pixel value + * + * Return: value to write to the x2r10g10b10 frame buffer for this palette entry + */ +static u32 get_bmp_col_x2r10g10b10(struct bmp_color_table_entry *cte) +{ + return ((cte->red << 22U) | + (cte->green << 12U) | + (cte->blue << 2U)); +} + /** * write_pix8() - Write a pixel from a BMP image into the framebuffer * @@ -42,8 +54,8 @@ static uint get_bmp_col_16bpp(struct bmp_color_table_entry cte) * which is either written directly (bpix == 8) or used to look up the * palette to get a colour to write */ -static void write_pix8(u8 *fb, uint bpix, struct bmp_color_table_entry *palette, - u8 *bmap) +static void write_pix8(u8 *fb, uint bpix, enum video_format eformat, + struct bmp_color_table_entry *palette, u8 *bmap) { if (bpix == 8) { *fb++ = *bmap; @@ -57,6 +69,8 @@ static void write_pix8(u8 *fb, uint bpix, struct bmp_color_table_entry *palette, *fb++ = cte->red; *fb++ = cte->green; *fb++ = cte->blue; + } else if (eformat == VIDEO_X2R10G10B10) { + *(u32 *)fb = get_bmp_col_x2r10g10b10(cte); } else { *fb++ = cte->blue; *fb++ = cte->green; @@ -66,28 +80,29 @@ static void write_pix8(u8 *fb, uint bpix, struct bmp_color_table_entry *palette, } } -static void draw_unencoded_bitmap(u8 **fbp, uint bpix, uchar *bmap, +static void draw_unencoded_bitmap(u8 **fbp, uint bpix, + enum video_format eformat, uchar *bmap, struct bmp_color_table_entry *palette, int cnt) { u8 *fb = *fbp; while (cnt > 0) { - write_pix8(fb, bpix, palette, bmap++); + write_pix8(fb, bpix, eformat, palette, bmap++); fb += bpix / 8; cnt--; } *fbp = fb; } -static void draw_encoded_bitmap(u8 **fbp, uint bpix, +static void draw_encoded_bitmap(u8 **fbp, uint bpix, enum video_format eformat, struct bmp_color_table_entry *palette, u8 *bmap, int cnt) { u8 *fb = *fbp; while (cnt > 0) { - write_pix8(fb, bpix, palette, bmap); + write_pix8(fb, bpix, eformat, palette, bmap); fb += bpix / 8; cnt--; } @@ -106,6 +121,7 @@ static void video_display_rle8_bitmap(struct udevice *dev, int x, y; int decode = 1; uint bytes_per_pixel = bpix / 8; + enum video_format eformat = priv->format; debug("%s\n", __func__); bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset); @@ -148,7 +164,7 @@ static void video_display_rle8_bitmap(struct udevice *dev, else cnt = runlen; draw_unencoded_bitmap( - &fb, bpix, + &fb, bpix, eformat, bmap, palette, cnt); } x += runlen; @@ -173,8 +189,9 @@ static void video_display_rle8_bitmap(struct udevice *dev, cnt = width - x; else cnt = runlen; - draw_encoded_bitmap(&fb, bpix, palette, - &bmap[1], cnt); + draw_encoded_bitmap(&fb, bpix, eformat, + palette, &bmap[1], + cnt); } x += runlen; } @@ -224,6 +241,7 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, unsigned long width, height, byte_width; unsigned long pwidth = priv->xsize; unsigned colours, bpix, bmp_bpix; + enum video_format eformat; struct bmp_color_table_entry *palette; int hdr_size; int ret; @@ -245,6 +263,7 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, colours = 1 << bmp_bpix; bpix = VNBITS(priv->bpix); + eformat = priv->format; if (bpix != 1 && bpix != 8 && bpix != 16 && bpix != 32) { printf("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n", @@ -312,7 +331,7 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, for (i = 0; i < height; ++i) { WATCHDOG_RESET(); for (j = 0; j < width; j++) { - write_pix8(fb, bpix, palette, bmap); + write_pix8(fb, bpix, eformat, palette, bmap); bmap++; fb += bpix / 8; } @@ -345,6 +364,16 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, (bmap[0] >> 3); bmap += 3; fb += 2; + } else if (eformat == VIDEO_X2R10G10B10) { + u32 pix; + + pix = *bmap++ << 2U; + pix |= *bmap++ << 12U; + pix |= *bmap++ << 22U; + *fb++ = pix & 0xff; + *fb++ = (pix >> 8) & 0xff; + *fb++ = (pix >> 16) & 0xff; + *fb++ = pix >> 24; } else { *fb++ = *bmap++; *fb++ = *bmap++; @@ -361,10 +390,23 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, if (IS_ENABLED(CONFIG_BMP_32BPP)) { for (i = 0; i < height; ++i) { for (j = 0; j < width; j++) { - *fb++ = *bmap++; - *fb++ = *bmap++; - *fb++ = *bmap++; - *fb++ = *bmap++; + if (eformat == VIDEO_X2R10G10B10) { + u32 pix; + + pix = *bmap++ << 2U; + pix |= *bmap++ << 12U; + pix |= *bmap++ << 22U; + pix |= (*bmap++ >> 6) << 30U; + *fb++ = pix & 0xff; + *fb++ = (pix >> 8) & 0xff; + *fb++ = (pix >> 16) & 0xff; + *fb++ = pix >> 24; + } else { + *fb++ = *bmap++; + *fb++ = *bmap++; + *fb++ = *bmap++; + *fb++ = *bmap++; + } } fb -= priv->line_length + width * (bpix / 8); } -- cgit v1.2.3 From bd0df8236987df03e6131f923e31c3d9f943d086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Wed, 9 Mar 2022 20:46:00 +0100 Subject: video: Allow drivers to allocate the frame buffer themselves MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When plat->base is set by driver then skip frame buffer reservation and allocation. Signed-off-by: Pali Rohár --- drivers/video/video-uclass.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c index 7d499bcec5..88797d4a21 100644 --- a/drivers/video/video-uclass.c +++ b/drivers/video/video-uclass.c @@ -33,7 +33,8 @@ * information represents the requires size and alignment of the frame buffer * for the device. The values can be an over-estimate but cannot be too * small. The actual values will be suppled (in the same manner) by the bind() - * method after relocation. + * method after relocation. Additionally driver can allocate frame buffer + * itself by setting plat->base. * * This information is then picked up by video_reserve() which works out how * much memory is needed for all devices. This is allocated between @@ -78,6 +79,10 @@ static ulong alloc_fb(struct udevice *dev, ulong *addrp) if (!plat->size) return 0; + /* Allow drivers to allocate the frame buffer themselves */ + if (plat->base) + return 0; + align = plat->align ? plat->align : 1 << 20; base = *addrp - plat->size; base &= ~(align - 1); -- cgit v1.2.3 From d6213e206c441a3b2b83c0588954c851c3610d97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Wed, 9 Mar 2022 20:46:01 +0100 Subject: Nokia RX-51: Convert to CONFIG_DM_VIDEO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mechanically convert video_hw_init() function to UCLASS_VIDEO probe callback and replace CONFIG_CFB_CONSOLE by CONFIG_DM_VIDEO. As framebuffer base address is setup by the bootloader which loads U-Boot, set plat->base to that fixed framebuffer address. This change was tested in qemu n900 machine and is working fine. What does not work is CONFIG_VIDEO_LOGO, seems to be buggy. Signed-off-by: Pali Rohár --- board/nokia/rx51/rx51.c | 40 ++++++++++++++++++++++++---------------- configs/nokia_rx51_defconfig | 7 +++++-- include/configs/nokia_rx51.h | 11 ++--------- 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/board/nokia/rx51/rx51.c b/board/nokia/rx51/rx51.c index a52691509d..621cff0956 100644 --- a/board/nokia/rx51/rx51.c +++ b/board/nokia/rx51/rx51.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include #include @@ -62,8 +62,6 @@ struct emu_hal_params_rx51 { DECLARE_GLOBAL_DATA_PTR; -GraphicDevice gdev; - const omap3_sysinfo sysinfo = { DDR_STACKED, "Nokia RX-51", @@ -342,22 +340,28 @@ void setup_board_tags(struct tag **in_params) *in_params = params; } -/* - * Routine: video_hw_init - * Description: Set up the GraphicDevice depending on sys_boot. - */ -void *video_hw_init(void) +static int rx51_video_probe(struct udevice *dev) { - /* fill in Graphic Device */ - gdev.frameAdrs = 0x8f9c0000; - gdev.winSizeX = 800; - gdev.winSizeY = 480; - gdev.gdfBytesPP = 2; - gdev.gdfIndex = GDF_16BIT_565RGB; - memset((void *)gdev.frameAdrs, 0, 0xbb800); - return (void *) &gdev; + struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev); + struct video_priv *uc_priv = dev_get_uclass_priv(dev); + + uc_plat->base = 0x8f9c0000; + uc_plat->size = 800 * 480 * sizeof(u16); + uc_priv->xsize = 800; + uc_priv->ysize = 480; + uc_priv->bpix = VIDEO_BPP16; + + video_set_flush_dcache(dev, true); + + return 0; } +U_BOOT_DRIVER(rx51_video) = { + .name = "rx51_video", + .id = UCLASS_VIDEO, + .probe = rx51_video_probe, +}; + /* * Routine: twl4030_regulator_set_mode * Description: Set twl4030 regulator mode over i2c powerbus. @@ -777,6 +781,10 @@ U_BOOT_DRVINFOS(rx51_watchdog) = { { "rx51_watchdog" }, }; +U_BOOT_DRVINFOS(rx51_video) = { + { "rx51_video" }, +}; + U_BOOT_DRVINFOS(rx51_kp) = { { "rx51_kp" }, }; diff --git a/configs/nokia_rx51_defconfig b/configs/nokia_rx51_defconfig index 47b7bc3b4f..1d64981afc 100644 --- a/configs/nokia_rx51_defconfig +++ b/configs/nokia_rx51_defconfig @@ -77,8 +77,11 @@ CONFIG_SPI=y CONFIG_USB=y CONFIG_USB_MUSB_UDC=y CONFIG_USB_OMAP3=y -CONFIG_CFB_CONSOLE=y -CONFIG_CFB_CONSOLE_ANSI=y +CONFIG_DM_VIDEO=y +CONFIG_VIDEO_LOGO=y +# CONFIG_VIDEO_BPP8 is not set +# CONFIG_VIDEO_BPP32 is not set +CONFIG_SYS_WHITE_ON_BLACK=y CONFIG_SPLASH_SCREEN=y CONFIG_WATCHDOG_TIMEOUT_MSECS=31000 CONFIG_WDT=y diff --git a/include/configs/nokia_rx51.h b/include/configs/nokia_rx51.h index 9be64c3d3f..e837b12b56 100644 --- a/include/configs/nokia_rx51.h +++ b/include/configs/nokia_rx51.h @@ -70,19 +70,12 @@ #define CONFIG_SYS_ONENAND_BASE ONENAND_MAP -/* - * Framebuffer - */ -/* Video console */ -#define VIDEO_FB_16BPP_PIXEL_SWAP -#define VIDEO_FB_16BPP_WORD_SWAP - /* Environment information */ #define CONFIG_EXTRA_ENV_SETTINGS \ "usbtty=cdc_acm\0" \ "stdin=usbtty,serial,keyboard\0" \ - "stdout=usbtty,serial,vga\0" \ - "stderr=usbtty,serial,vga\0" \ + "stdout=usbtty,serial,vidconsole\0" \ + "stderr=usbtty,serial,vidconsole\0" \ "slide=gpio input " __stringify(GPIO_SLIDE) "\0" \ "switchmmc=mmc dev ${mmcnum}\0" \ "kernaddr=0x82008000\0" \ -- cgit v1.2.3 From 9baff975bf88729446876d4cd582bc5466707d3b Mon Sep 17 00:00:00 2001 From: Nam Nguyen Date: Tue, 18 Jan 2022 10:28:15 +0700 Subject: configs: falcon: Enabled I2C support for R-Car V3U Enable I2C support for R-Car V3U (R8A779A0) on Falcon board. Signed-off-by: Nam Nguyen Signed-off-by: Hai Pham Signed-off-by: Marek Vasut --- configs/r8a779a0_falcon_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/r8a779a0_falcon_defconfig b/configs/r8a779a0_falcon_defconfig index 32e218bbda..9fce6c26f9 100644 --- a/configs/r8a779a0_falcon_defconfig +++ b/configs/r8a779a0_falcon_defconfig @@ -47,7 +47,7 @@ CONFIG_CLK=y CONFIG_CLK_RENESAS=y CONFIG_RCAR_GPIO=y CONFIG_DM_I2C=y -CONFIG_SYS_I2C_RCAR_IIC=y +CONFIG_SYS_I2C_RCAR_I2C=y CONFIG_MMC_IO_VOLTAGE=y CONFIG_MMC_UHS_SUPPORT=y CONFIG_MMC_HS200_SUPPORT=y -- cgit v1.2.3 From 098579a4a5d615e80543a3c34855cecf4864d4d2 Mon Sep 17 00:00:00 2001 From: Nam Nguyen Date: Tue, 18 Jan 2022 10:33:40 +0700 Subject: configs: eagle: Enabled I2C support for R-Car V3M Enable I2C support for R-Car V3M (R8A77970) on Eagle board. Signed-off-by: Nam Nguyen Signed-off-by: Hai Pham Signed-off-by: Marek Vasut --- configs/r8a77970_eagle_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/r8a77970_eagle_defconfig b/configs/r8a77970_eagle_defconfig index 70b23da174..4ccc6f1c36 100644 --- a/configs/r8a77970_eagle_defconfig +++ b/configs/r8a77970_eagle_defconfig @@ -51,7 +51,7 @@ CONFIG_DFU_RAM=y CONFIG_DFU_SF=y CONFIG_RCAR_GPIO=y CONFIG_DM_I2C=y -CONFIG_SYS_I2C_RCAR_IIC=y +CONFIG_SYS_I2C_RCAR_I2C=y # CONFIG_MMC is not set CONFIG_MTD=y CONFIG_DM_SPI_FLASH=y -- cgit v1.2.3 From 23fd0bb987c781d2f87af6b546fb219b40ad1057 Mon Sep 17 00:00:00 2001 From: Nam Nguyen Date: Tue, 18 Jan 2022 10:35:37 +0700 Subject: configs: condor: Enabled I2C support for R-Car V3H Enable I2C support for R-Car V3H (R8A77980) on Condor board. Signed-off-by: Nam Nguyen Signed-off-by: Hai Pham Signed-off-by: Marek Vasut --- configs/r8a77980_condor_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/r8a77980_condor_defconfig b/configs/r8a77980_condor_defconfig index 194fdde812..36b39c1461 100644 --- a/configs/r8a77980_condor_defconfig +++ b/configs/r8a77980_condor_defconfig @@ -52,7 +52,7 @@ CONFIG_DFU_RAM=y CONFIG_DFU_SF=y CONFIG_RCAR_GPIO=y CONFIG_DM_I2C=y -CONFIG_SYS_I2C_RCAR_IIC=y +CONFIG_SYS_I2C_RCAR_I2C=y # CONFIG_MMC is not set CONFIG_MTD=y CONFIG_DM_SPI_FLASH=y -- cgit v1.2.3 From 2addee35f2da178bf858403f2e2cb20a59df734f Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Mon, 14 Mar 2022 09:32:53 +0100 Subject: usb: dwc2: handle return code of dev_read_size() in of to, plat function dev_read_size() returns -EINVAL (-22) if the property "g-tx-fifo-size" does not exist. If that's the case, we now keep the default value of 0. Signed-off-by: Wolfgang Grandegger --- drivers/usb/gadget/dwc2_udc_otg.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/dwc2_udc_otg.c b/drivers/usb/gadget/dwc2_udc_otg.c index 2748270ad6..77988f78ab 100644 --- a/drivers/usb/gadget/dwc2_udc_otg.c +++ b/drivers/usb/gadget/dwc2_udc_otg.c @@ -996,8 +996,9 @@ static int dwc2_udc_otg_of_to_plat(struct udevice *dev) plat->rx_fifo_sz = dev_read_u32_default(dev, "g-rx-fifo-size", 0); plat->np_tx_fifo_sz = dev_read_u32_default(dev, "g-np-tx-fifo-size", 0); - plat->tx_fifo_sz_nb = - dev_read_size(dev, "g-tx-fifo-size") / sizeof(u32); + ret = dev_read_size(dev, "g-tx-fifo-size"); + if (ret > 0) + plat->tx_fifo_sz_nb = ret / sizeof(u32); if (plat->tx_fifo_sz_nb > DWC2_MAX_HW_ENDPOINTS) plat->tx_fifo_sz_nb = DWC2_MAX_HW_ENDPOINTS; if (plat->tx_fifo_sz_nb) { -- cgit v1.2.3 From 7ce4f1fad28e538d216065b5042f6f20a5f50a90 Mon Sep 17 00:00:00 2001 From: Christophe Kerello Date: Tue, 22 Feb 2022 17:38:49 +0100 Subject: mtd: rawnand: stm32_fmc2: add NAND Write Protect support This patch adds the support of the WP# signal. WP will be disabled before the first access to the NAND flash. Signed-off-by: Christophe Kerello Reviewed-by: Patrice Chotard --- drivers/mtd/nand/raw/stm32_fmc2_nand.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c index eee65949d7..fb3279b405 100644 --- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c +++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -149,6 +150,7 @@ struct stm32_fmc2_timings { struct stm32_fmc2_nand { struct nand_chip chip; struct stm32_fmc2_timings timings; + struct gpio_desc wp_gpio; int ncs; int cs_used[FMC2_MAX_CE]; }; @@ -824,6 +826,9 @@ static int stm32_fmc2_nfc_parse_child(struct stm32_fmc2_nfc *nfc, ofnode node) nand->cs_used[i] = cs[i]; } + gpio_request_by_name_nodev(node, "wp-gpios", 0, &nand->wp_gpio, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + nand->chip.flash_node = node; return 0; @@ -972,6 +977,10 @@ static int stm32_fmc2_nfc_probe(struct udevice *dev) chip->ecc.size = FMC2_ECC_STEP_SIZE; chip->ecc.strength = FMC2_ECC_BCH8; + /* Disable Write Protect */ + if (dm_gpio_is_valid(&nand->wp_gpio)) + dm_gpio_set_value(&nand->wp_gpio, 0); + ret = nand_scan_ident(mtd, nand->ncs, NULL); if (ret) return ret; -- cgit v1.2.3 From 0c20f53b3ff93d261a546601ad7190edf62f534b Mon Sep 17 00:00:00 2001 From: Patrick Delaunay Date: Tue, 15 Feb 2022 16:08:50 +0100 Subject: stm32mp: bsec: add permanent lock write support Add support of the permanent lock support in U-Boot proper when BSEC is not managed by secure monitor (TF-A SP_MIN or OP-TEE). This patch avoid issue with stm32key command and fuse command on basic boot for this missing feature of U-Boot BSEC driver. Reported-by: Johann Neuhauser Signed-off-by: Patrick Delaunay Tested-by: Johann Neuhauser Reviewed-by: Patrice Chotard --- arch/arm/mach-stm32mp/bsec.c | 90 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-stm32mp/bsec.c b/arch/arm/mach-stm32mp/bsec.c index 27d1829501..fd6e1a3957 100644 --- a/arch/arm/mach-stm32mp/bsec.c +++ b/arch/arm/mach-stm32mp/bsec.c @@ -18,6 +18,7 @@ #include #define BSEC_OTP_MAX_VALUE 95 +#define BSEC_OTP_UPPER_START 32 #define BSEC_TIMEOUT_US 10000 /* BSEC REGISTER OFFSET (base relative) */ @@ -41,6 +42,7 @@ /* BSEC_CONTROL Register */ #define BSEC_READ 0x000 #define BSEC_WRITE 0x100 +#define BSEC_LOCK 0x200 /* LOCK Register */ #define OTP_LOCK_MASK 0x1F @@ -61,6 +63,11 @@ */ #define BSEC_LOCK_PROGRAM 0x04 +/* + * OTP status: bit 0 permanent lock + */ +#define BSEC_LOCK_PERM BIT(0) + /** * bsec_lock() - manage lock for each type SR/SP/SW * @address: address of bsec IP register @@ -284,6 +291,65 @@ static int bsec_program_otp(struct udevice *dev, long base, u32 val, u32 otp) return ret; } +/** + * bsec_permanent_lock_otp() - permanent lock of OTP in SAFMEM + * @dev: bsec IP device + * @base: base address of bsec IP + * @otp: otp number (0 - BSEC_OTP_MAX_VALUE) + * Return: 0 if no error + */ +static int bsec_permanent_lock_otp(struct udevice *dev, long base, uint32_t otp) +{ + int ret; + bool power_up = false; + u32 val, addr; + + /* check if safemem is power up */ + if (!(readl(base + BSEC_OTP_STATUS_OFF) & BSEC_MODE_PWR_MASK)) { + ret = bsec_power_safmem(base, true); + if (ret) + return ret; + + power_up = true; + } + + /* + * low OTPs = 2 bits word for low OTPs, 1 bits per word for upper OTP + * and only 16 bits used in WRDATA + */ + if (otp < BSEC_OTP_UPPER_START) { + addr = otp / 8; + val = 0x03 << ((otp * 2) & 0xF); + } else { + addr = BSEC_OTP_UPPER_START / 8 + + ((otp - BSEC_OTP_UPPER_START) / 16); + val = 0x01 << (otp & 0xF); + } + + /* set value in write register*/ + writel(val, base + BSEC_OTP_WRDATA_OFF); + + /* set BSEC_OTP_CTRL_OFF with the otp addr and lock request*/ + writel(addr | BSEC_WRITE | BSEC_LOCK, base + BSEC_OTP_CTRL_OFF); + + /* check otp status*/ + ret = readl_poll_timeout(base + BSEC_OTP_STATUS_OFF, + val, (val & BSEC_MODE_BUSY_MASK) == 0, + BSEC_TIMEOUT_US); + if (ret) + return ret; + + if (val & BSEC_MODE_PROGFAIL_MASK) + ret = -EACCES; + else + ret = bsec_check_error(base, otp); + + if (power_up) + bsec_power_safmem(base, false); + + return ret; +} + /* BSEC MISC driver *******************************************************/ struct stm32mp_bsec_plat { u32 base; @@ -339,9 +405,14 @@ static int stm32mp_bsec_read_shadow(struct udevice *dev, u32 *val, u32 otp) static int stm32mp_bsec_read_lock(struct udevice *dev, u32 *val, u32 otp) { struct stm32mp_bsec_plat *plat = dev_get_plat(dev); + u32 wrlock; /* return OTP permanent write lock status */ - *val = bsec_read_lock(plat->base + BSEC_WRLOCK_OFF, otp); + wrlock = bsec_read_lock(plat->base + BSEC_WRLOCK_OFF, otp); + + *val = 0; + if (wrlock) + *val = BSEC_LOCK_PERM; return 0; } @@ -377,15 +448,22 @@ static int stm32mp_bsec_write_shadow(struct udevice *dev, u32 val, u32 otp) static int stm32mp_bsec_write_lock(struct udevice *dev, u32 val, u32 otp) { - if (!IS_ENABLED(CONFIG_ARM_SMCCC) || IS_ENABLED(CONFIG_SPL_BUILD)) - return -ENOTSUPP; + struct stm32mp_bsec_plat *plat; + + /* only permanent write lock is supported in U-Boot */ + if (!(val & BSEC_LOCK_PERM)) { + dev_dbg(dev, "lock option without BSEC_LOCK_PERM: %x\n", val); + return 0; /* nothing to do */ + } - if (val == 1) + if (IS_ENABLED(CONFIG_ARM_SMCCC) && !IS_ENABLED(CONFIG_SPL_BUILD)) return stm32_smc_exec(STM32_SMC_BSEC, STM32_SMC_WRLOCK_OTP, otp, 0); - if (val == 0) - return 0; /* nothing to do */ + + plat = dev_get_plat(dev); + + return bsec_permanent_lock_otp(dev, plat->base, otp); return -EINVAL; } -- cgit v1.2.3 From 2d48d99c4a90750b80438314bae20499b4ce6705 Mon Sep 17 00:00:00 2001 From: Patrick Delaunay Date: Tue, 15 Feb 2022 16:08:51 +0100 Subject: stm32mp1: bsec: add missing dev in function comment Add the missing @dev reference in some function description. Fixes: b66bfdf238b9 ("arm: stm32mp: bsec: migrate trace to log macro") Signed-off-by: Patrick Delaunay Reviewed-by: Patrice Chotard --- arch/arm/mach-stm32mp/bsec.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/mach-stm32mp/bsec.c b/arch/arm/mach-stm32mp/bsec.c index fd6e1a3957..506caa0a31 100644 --- a/arch/arm/mach-stm32mp/bsec.c +++ b/arch/arm/mach-stm32mp/bsec.c @@ -167,6 +167,7 @@ static int bsec_power_safmem(u32 base, bool power) /** * bsec_shadow_register() - copy safmen otp to bsec data + * @dev: bsec IP device * @base: base address of bsec IP * @otp: otp number (0 - BSEC_OTP_MAX_VALUE) * Return: 0 if no error @@ -210,6 +211,7 @@ static int bsec_shadow_register(struct udevice *dev, u32 base, u32 otp) /** * bsec_read_shadow() - read an otp data value from shadow + * @dev: bsec IP device * @base: base address of bsec IP * @val: read value * @otp: otp number (0 - BSEC_OTP_MAX_VALUE) @@ -224,6 +226,7 @@ static int bsec_read_shadow(struct udevice *dev, u32 base, u32 *val, u32 otp) /** * bsec_write_shadow() - write value in BSEC data register in shadow + * @dev: bsec IP device * @base: base address of bsec IP * @val: value to write * @otp: otp number (0 - BSEC_OTP_MAX_VALUE) @@ -242,6 +245,7 @@ static int bsec_write_shadow(struct udevice *dev, u32 base, u32 val, u32 otp) /** * bsec_program_otp() - program a bit in SAFMEM + * @dev: bsec IP device * @base: base address of bsec IP * @val: value to program * @otp: otp number (0 - BSEC_OTP_MAX_VALUE) -- cgit v1.2.3 From 6ed21f3d704370d5b1a5db8ee953fd7c5cea08d6 Mon Sep 17 00:00:00 2001 From: Patrick Delaunay Date: Tue, 1 Feb 2022 14:37:19 +0100 Subject: board: st: common: update test on misc_read result in command stboard Update management of misc_read/misc_write, which now returns length of data after the commit 8729b1ae2cbd ("misc: Update read() and write() methods to return bytes xfered"): raise a error when the result is not the expected length. Fixes: 658fde8a36ff ("board: stm32mp1: stboard: lock the OTP after programming") Signed-off-by: Patrick Delaunay Reviewed-by: Patrice Chotard --- board/st/common/cmd_stboard.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/board/st/common/cmd_stboard.c b/board/st/common/cmd_stboard.c index 2fba383168..c1ecd643b0 100644 --- a/board/st/common/cmd_stboard.c +++ b/board/st/common/cmd_stboard.c @@ -91,14 +91,14 @@ static int do_stboard(struct cmd_tbl *cmdtp, int flag, int argc, ret = misc_read(dev, STM32_BSEC_OTP(BSEC_OTP_BOARD), &otp, sizeof(otp)); - if (ret < 0) { + if (ret != sizeof(otp)) { puts("OTP read error"); return CMD_RET_FAILURE; } ret = misc_read(dev, STM32_BSEC_LOCK(BSEC_OTP_BOARD), &lock, sizeof(lock)); - if (ret < 0) { + if (ret != sizeof(lock)) { puts("LOCK read error"); return CMD_RET_FAILURE; } @@ -172,7 +172,7 @@ static int do_stboard(struct cmd_tbl *cmdtp, int flag, int argc, ret = misc_write(dev, STM32_BSEC_OTP(BSEC_OTP_BOARD), &otp, sizeof(otp)); - if (ret < 0) { + if (ret != sizeof(otp)) { puts("BOARD programming error\n"); return CMD_RET_FAILURE; } @@ -181,7 +181,7 @@ static int do_stboard(struct cmd_tbl *cmdtp, int flag, int argc, otp = 1; ret = misc_write(dev, STM32_BSEC_LOCK(BSEC_OTP_BOARD), &otp, sizeof(otp)); - if (ret < 0) { + if (ret != sizeof(otp)) { puts("BOARD lock error\n"); return CMD_RET_FAILURE; } -- cgit v1.2.3 From 310ef93028014914efd9c5a210443b378670f5e3 Mon Sep 17 00:00:00 2001 From: Gabriel Fernandez Date: Tue, 1 Feb 2022 14:02:14 +0100 Subject: video: stm32: stm32_ltdc: fix the check of return value of clk_set_rate() The clk_set_rate() function returns rate as an 'ulong' not an 'int' and rate > 0 by default. This patch avoids to display the associated warning when the set rate function returns the new frequency. Fixes: aeaf330649e8 ("video: stm32: stm32_ltdc: add bridge to display controller") Signed-off-by: Gabriel Fernandez Signed-off-by: Patrick Delaunay Reviewed-by: Patrice Chotard --- drivers/video/stm32/stm32_ltdc.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/video/stm32/stm32_ltdc.c b/drivers/video/stm32/stm32_ltdc.c index 87e5fd54d9..e741e74739 100644 --- a/drivers/video/stm32/stm32_ltdc.c +++ b/drivers/video/stm32/stm32_ltdc.c @@ -338,6 +338,7 @@ static int stm32_ltdc_probe(struct udevice *dev) struct display_timing timings; struct clk pclk; struct reset_ctl rst; + ulong rate; int ret; priv->regs = (void *)dev_read_addr(dev); @@ -375,13 +376,13 @@ static int stm32_ltdc_probe(struct udevice *dev) } } - ret = clk_set_rate(&pclk, timings.pixelclock.typ); - if (ret) - dev_warn(dev, "fail to set pixel clock %d hz\n", - timings.pixelclock.typ); + rate = clk_set_rate(&pclk, timings.pixelclock.typ); + if (IS_ERR_VALUE(rate)) + dev_warn(dev, "fail to set pixel clock %d hz, ret=%ld\n", + timings.pixelclock.typ, rate); dev_dbg(dev, "Set pixel clock req %d hz get %ld hz\n", - timings.pixelclock.typ, clk_get_rate(&pclk)); + timings.pixelclock.typ, rate); ret = reset_get_by_index(dev, 0, &rst); if (ret) { -- cgit v1.2.3 From 182738fe2f65b6b7faab70d883ad1ab435c6a8b5 Mon Sep 17 00:00:00 2001 From: Patrick Delaunay Date: Mon, 31 Jan 2022 16:07:54 +0100 Subject: arm: dts: stm32mp15: alignment with v5.17 Device tree alignment with Linux kernel v5.17-rc1 - ARM: dts: stm32: add pull-up to USART3 and UART7 RX pins on STM32MP15 DKx boards - ARM: dts: stm32: clean uart4_idle_pins_a node for stm32mp15 - ARM: dts: stm32: tune the HS USB PHYs on stm32mp15xx-dkx - ARM: dts: stm32: tune the HS USB PHYs on stm32mp157c-ev1 - ARM: dts: stm32: fix stusb1600 pinctrl used on stm32mp157c-dk Signed-off-by: Patrick Delaunay Reviewed-by: Patrice Chotard --- arch/arm/dts/stm32mp15-pinctrl.dtsi | 32 ++++++++++++++++---------------- arch/arm/dts/stm32mp157c-ev1.dts | 22 ++++++++++++++++++++++ arch/arm/dts/stm32mp15xx-dkx.dtsi | 16 ++++++++++++++++ 3 files changed, 54 insertions(+), 16 deletions(-) diff --git a/arch/arm/dts/stm32mp15-pinctrl.dtsi b/arch/arm/dts/stm32mp15-pinctrl.dtsi index d3553e0f01..6161f5906e 100644 --- a/arch/arm/dts/stm32mp15-pinctrl.dtsi +++ b/arch/arm/dts/stm32mp15-pinctrl.dtsi @@ -1718,7 +1718,7 @@ stusb1600_pins_a: stusb1600-0 { pins { - pinmux = ; + pinmux = ; bias-pull-up; }; }; @@ -1737,20 +1737,20 @@ }; uart4_idle_pins_a: uart4-idle-0 { - pins1 { - pinmux = ; /* UART4_TX */ - }; - pins2 { - pinmux = ; /* UART4_RX */ - bias-disable; - }; + pins1 { + pinmux = ; /* UART4_TX */ + }; + pins2 { + pinmux = ; /* UART4_RX */ + bias-disable; + }; }; uart4_sleep_pins_a: uart4-sleep-0 { - pins { + pins { pinmux = , /* UART4_TX */ ; /* UART4_RX */ - }; + }; }; uart4_pins_b: uart4-1 { @@ -1816,7 +1816,7 @@ }; pins2 { pinmux = ; /* UART7_RX */ - bias-disable; + bias-pull-up; }; }; @@ -1826,7 +1826,7 @@ }; pins2 { pinmux = ; /* UART7_RX */ - bias-disable; + bias-pull-up; }; }; @@ -1971,7 +1971,7 @@ pins2 { pinmux = , /* USART3_RX */ ; /* USART3_CTS_NSS */ - bias-disable; + bias-pull-up; }; }; @@ -1988,7 +1988,7 @@ }; pins3 { pinmux = ; /* USART3_RX */ - bias-disable; + bias-pull-up; }; }; @@ -2012,7 +2012,7 @@ pins2 { pinmux = , /* USART3_RX */ ; /* USART3_CTS_NSS */ - bias-disable; + bias-pull-up; }; }; @@ -2029,7 +2029,7 @@ }; pins3 { pinmux = ; /* USART3_RX */ - bias-disable; + bias-pull-up; }; }; diff --git a/arch/arm/dts/stm32mp157c-ev1.dts b/arch/arm/dts/stm32mp157c-ev1.dts index 5c5b1ddf7b..e222d2d2cb 100644 --- a/arch/arm/dts/stm32mp157c-ev1.dts +++ b/arch/arm/dts/stm32mp157c-ev1.dts @@ -375,3 +375,25 @@ &usbphyc { status = "okay"; }; + +&usbphyc_port0 { + st,tune-hs-dc-level = <2>; + st,enable-fs-rftime-tuning; + st,enable-hs-rftime-reduction; + st,trim-hs-current = <15>; + st,trim-hs-impedance = <1>; + st,tune-squelch-level = <3>; + st,tune-hs-rx-offset = <2>; + st,no-lsfs-sc; +}; + +&usbphyc_port1 { + st,tune-hs-dc-level = <2>; + st,enable-fs-rftime-tuning; + st,enable-hs-rftime-reduction; + st,trim-hs-current = <15>; + st,trim-hs-impedance = <1>; + st,tune-squelch-level = <3>; + st,tune-hs-rx-offset = <2>; + st,no-lsfs-sc; +}; diff --git a/arch/arm/dts/stm32mp15xx-dkx.dtsi b/arch/arm/dts/stm32mp15xx-dkx.dtsi index 5502eec94b..f8130bf445 100644 --- a/arch/arm/dts/stm32mp15xx-dkx.dtsi +++ b/arch/arm/dts/stm32mp15xx-dkx.dtsi @@ -702,10 +702,26 @@ &usbphyc_port0 { phy-supply = <&vdd_usb>; + st,tune-hs-dc-level = <2>; + st,enable-fs-rftime-tuning; + st,enable-hs-rftime-reduction; + st,trim-hs-current = <15>; + st,trim-hs-impedance = <1>; + st,tune-squelch-level = <3>; + st,tune-hs-rx-offset = <2>; + st,no-lsfs-sc; }; &usbphyc_port1 { phy-supply = <&vdd_usb>; + st,tune-hs-dc-level = <2>; + st,enable-fs-rftime-tuning; + st,enable-hs-rftime-reduction; + st,trim-hs-current = <15>; + st,trim-hs-impedance = <1>; + st,tune-squelch-level = <3>; + st,tune-hs-rx-offset = <2>; + st,no-lsfs-sc; }; &vrefbuf { -- cgit v1.2.3 From 27f6c653ae30ee60fe777a37098c8ffa714d8a49 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 28 Jan 2022 19:35:20 +0100 Subject: ARM: dts: stm32: Add USB OTG pinctrl and regulator nodes into SPL DT on DHCOR Fix the following warning in SPL and make sure that even DTs which enforce Vbus detection using u-boot,force-vbus-detection;, the DFU in SPL will work. dwc2-udc-otg usb-otg@49000000: prop pinctrl-0 index 0 invalid phandle Signed-off-by: Marek Vasut Cc: Patrice Chotard Cc: Patrick Delaunay Reviewed-by: Patrice Chotard --- arch/arm/dts/stm32mp15xx-dhcor-u-boot.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/dts/stm32mp15xx-dhcor-u-boot.dtsi b/arch/arm/dts/stm32mp15xx-dhcor-u-boot.dtsi index 338b674368..cd3190cca6 100644 --- a/arch/arm/dts/stm32mp15xx-dhcor-u-boot.dtsi +++ b/arch/arm/dts/stm32mp15xx-dhcor-u-boot.dtsi @@ -179,6 +179,14 @@ u-boot,dm-spl; }; +&usb33 { + u-boot,dm-spl; +}; + +&usbotg_hs_pins_a { + u-boot,dm-spl; +}; + &usbotg_hs { u-boot,dm-spl; }; -- cgit v1.2.3 From 9a8996df050dc20ef6954df077186400cb62622e Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 3 Feb 2022 02:49:29 +0100 Subject: ARM: dts: stm32: Move vdd_io extras into Avenger96 extras The vdd_io regulator is present only on DHCOR SoM configured for 1V8 IO, as populated on Avenger96, but not present on 3V3 DHCOR SoM. Move these extras to Avenger96 u-boot DT extras. Fixes: 3919aa1722a ("ARM: dts: stm32: Add DFU support for DHCOR recovery") Signed-off-by: Marek Vasut Cc: Patrice Chotard Cc: Patrick Delaunay Reviewed-by: Patrice Chotard Reviewed-by: Patrick Delaunay --- arch/arm/dts/stm32mp15xx-dhcor-avenger96-u-boot.dtsi | 4 ++++ arch/arm/dts/stm32mp15xx-dhcor-u-boot.dtsi | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/dts/stm32mp15xx-dhcor-avenger96-u-boot.dtsi b/arch/arm/dts/stm32mp15xx-dhcor-avenger96-u-boot.dtsi index 6e6543b5e4..5bed53e3fd 100644 --- a/arch/arm/dts/stm32mp15xx-dhcor-avenger96-u-boot.dtsi +++ b/arch/arm/dts/stm32mp15xx-dhcor-avenger96-u-boot.dtsi @@ -101,3 +101,7 @@ u-boot,force-b-session-valid; hnp-srp-disable; }; + +&vdd_io { + u-boot,dm-spl; +}; diff --git a/arch/arm/dts/stm32mp15xx-dhcor-u-boot.dtsi b/arch/arm/dts/stm32mp15xx-dhcor-u-boot.dtsi index cd3190cca6..19f4221f87 100644 --- a/arch/arm/dts/stm32mp15xx-dhcor-u-boot.dtsi +++ b/arch/arm/dts/stm32mp15xx-dhcor-u-boot.dtsi @@ -203,10 +203,6 @@ u-boot,dm-spl; }; -&vdd_io { - u-boot,dm-spl; -}; - &vdd_usb { u-boot,dm-spl; }; -- cgit v1.2.3 From 0d44ad8bb4ee2e03f8e6b6f1cdd9f49c79d3dd65 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 25 Feb 2022 02:15:58 +0100 Subject: ram: stm32mp1: Unconditionally enable ASR Enable DRAM ASR, auto self-refresh, unconditionally. This saves non-trivial amount of power both at runtime and in suspend (on 2x W632GU6NB-15 ~150mW). Reviewed-by: Patrice Chotard Signed-off-by: Marek Vasut Cc: Patrick Delaunay Cc: Patrice Chotard --- drivers/ram/stm32mp1/stm32mp1_ddr.c | 25 +++++++++++++++++++++++++ drivers/ram/stm32mp1/stm32mp1_ddr_regs.h | 6 ++++++ 2 files changed, 31 insertions(+) diff --git a/drivers/ram/stm32mp1/stm32mp1_ddr.c b/drivers/ram/stm32mp1/stm32mp1_ddr.c index 4d78aa5cb1..528a171b45 100644 --- a/drivers/ram/stm32mp1/stm32mp1_ddr.c +++ b/drivers/ram/stm32mp1/stm32mp1_ddr.c @@ -27,6 +27,8 @@ #define RCC_DDRITFCR_DPHYAPBRST (BIT(17)) #define RCC_DDRITFCR_DPHYRST (BIT(18)) #define RCC_DDRITFCR_DPHYCTLRST (BIT(19)) +#define RCC_DDRITFCR_DDRCKMOD_MASK GENMASK(22, 20) +#define RCC_DDRITFCR_DDRCKMOD_ASR BIT(20) struct reg_desc { const char *name; @@ -651,6 +653,26 @@ static void stm32mp1_refresh_restore(struct stm32mp1_ddrctl *ctl, wait_sw_done_ack(ctl); } +static void stm32mp1_asr_enable(struct ddr_info *priv) +{ + struct stm32mp1_ddrctl *ctl = priv->ctl; + + clrsetbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCKMOD_MASK, + RCC_DDRITFCR_DDRCKMOD_ASR); + + start_sw_done(ctl); + + setbits_le32(&ctl->hwlpctl, DDRCTRL_HWLPCTL_HW_LP_EN); + writel(DDRCTRL_PWRTMG_POWERDOWN_TO_X32(0x10) | + DDRCTRL_PWRTMG_SELFREF_TO_X32(0x01), + &ctl->pwrtmg); + setbits_le32(&ctl->pwrctl, DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE); + setbits_le32(&ctl->pwrctl, DDRCTRL_PWRCTL_SELFREF_EN); + + setbits_le32(&ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + wait_sw_done_ack(ctl); +} + /* board-specific DDR power initializations. */ __weak int board_ddr_power_init(enum ddr_type ddr_type) { @@ -822,6 +844,9 @@ start: stm32mp1_refresh_restore(priv->ctl, config->c_reg.rfshctl3, config->c_reg.pwrctl); +/* Enable auto-self-refresh, which saves a bit of power at runtime. */ + stm32mp1_asr_enable(priv); + /* enable uMCTL2 AXI port 0 and 1 */ setbits_le32(&priv->ctl->pctrl_0, DDRCTRL_PCTRL_N_PORT_EN); setbits_le32(&priv->ctl->pctrl_1, DDRCTRL_PCTRL_N_PORT_EN); diff --git a/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h b/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h index f1a26e31f6..42be1ba57c 100644 --- a/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h +++ b/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h @@ -265,8 +265,14 @@ struct stm32mp1_ddrphy { #define DDRCTRL_PWRCTL_SELFREF_EN BIT(0) #define DDRCTRL_PWRCTL_POWERDOWN_EN BIT(1) +#define DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE BIT(3) #define DDRCTRL_PWRCTL_SELFREF_SW BIT(5) +#define DDRCTRL_PWRTMG_SELFREF_TO_X32(n) (((n) & 0xff) << 16) +#define DDRCTRL_PWRTMG_POWERDOWN_TO_X32(n) ((n) & 0x1f) + +#define DDRCTRL_HWLPCTL_HW_LP_EN BIT(0) + #define DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH BIT(0) #define DDRCTRL_RFSHTMG_T_RFC_NOM_X1_X32_MASK GENMASK(27, 16) -- cgit v1.2.3 From b49105320a5b1e6c5f88dd90766d65a79f9f4e65 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 25 Feb 2022 02:15:59 +0100 Subject: stm32mp: psci: Implement PSCI system suspend and DRAM SSR Implement PSCI system suspend and placement of DRAM into SSR while the CPUs are in suspend. This saves non-trivial amount of power in suspend, on 2x W632GU6NB-15 ~710mW. Reviewed-by: Patrice Chotard Signed-off-by: Marek Vasut Cc: Patrick Delaunay Cc: Patrice Chotard --- arch/arm/mach-stm32mp/include/mach/stm32.h | 3 + arch/arm/mach-stm32mp/psci.c | 527 ++++++++++++++++++++++++++++- 2 files changed, 519 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-stm32mp/include/mach/stm32.h b/arch/arm/mach-stm32mp/include/mach/stm32.h index c11a9903f2..47e88fc3dc 100644 --- a/arch/arm/mach-stm32mp/include/mach/stm32.h +++ b/arch/arm/mach-stm32mp/include/mach/stm32.h @@ -16,8 +16,11 @@ */ #define STM32_RCC_BASE 0x50000000 #define STM32_PWR_BASE 0x50001000 +#define STM32_SYSCFG_BASE 0x50020000 #define STM32_DBGMCU_BASE 0x50081000 #define STM32_FMC2_BASE 0x58002000 +#define STM32_DDRCTRL_BASE 0x5A003000 +#define STM32_DDRPHYC_BASE 0x5A004000 #define STM32_TZC_BASE 0x5C006000 #define STM32_ETZPC_BASE 0x5C007000 #define STM32_STGEN_BASE 0x5C008000 diff --git a/arch/arm/mach-stm32mp/psci.c b/arch/arm/mach-stm32mp/psci.c index 155aa79cd5..86c160987a 100644 --- a/arch/arm/mach-stm32mp/psci.c +++ b/arch/arm/mach-stm32mp/psci.c @@ -11,19 +11,152 @@ #include #include #include +#include #include -#define BOOT_API_A7_CORE0_MAGIC_NUMBER 0xCA7FACE0 -#define BOOT_API_A7_CORE1_MAGIC_NUMBER 0xCA7FACE1 - -#define MPIDR_AFF0 GENMASK(7, 0) - -#define RCC_MP_GRSTCSETR (STM32_RCC_BASE + 0x0404) -#define RCC_MP_GRSTCSETR_MPUP1RST BIT(5) -#define RCC_MP_GRSTCSETR_MPUP0RST BIT(4) -#define RCC_MP_GRSTCSETR_MPSYSRST BIT(0) - -#define STM32MP1_PSCI_NR_CPUS 2 +/* PWR */ +#define PWR_CR3 0x0c +#define PWR_MPUCR 0x10 + +#define PWR_CR3_DDRSREN BIT(10) +#define PWR_CR3_DDRRETEN BIT(12) + +#define PWR_MPUCR_PDDS BIT(0) +#define PWR_MPUCR_CSTDBYDIS BIT(3) +#define PWR_MPUCR_CSSF BIT(9) + +/* RCC */ +#define RCC_DDRITFCR 0xd8 + +#define RCC_DDRITFCR_DDRC1EN BIT(0) +#define RCC_DDRITFCR_DDRC1LPEN BIT(1) +#define RCC_DDRITFCR_DDRC2EN BIT(2) +#define RCC_DDRITFCR_DDRC2LPEN BIT(3) +#define RCC_DDRITFCR_DDRPHYCEN BIT(4) +#define RCC_DDRITFCR_DDRPHYCLPEN BIT(5) +#define RCC_DDRITFCR_DDRCAPBEN BIT(6) +#define RCC_DDRITFCR_DDRCAPBLPEN BIT(7) +#define RCC_DDRITFCR_AXIDCGEN BIT(8) +#define RCC_DDRITFCR_DDRPHYCAPBEN BIT(9) +#define RCC_DDRITFCR_DDRPHYCAPBLPEN BIT(10) +#define RCC_DDRITFCR_DDRCKMOD_MASK GENMASK(22, 20) +#define RCC_DDRITFCR_GSKPCTRL BIT(24) + +#define RCC_MP_SREQSETR 0x104 +#define RCC_MP_SREQCLRR 0x108 + +#define RCC_MP_CIER 0x414 +#define RCC_MP_CIFR 0x418 +#define RCC_MP_CIFR_WKUPF BIT(20) + +/* SYSCFG */ +#define SYSCFG_CMPCR 0x20 +#define SYSCFG_CMPCR_SW_CTRL BIT(2) +#define SYSCFG_CMPENSETR 0x24 +#define SYSCFG_CMPENCLRR 0x28 +#define SYSCFG_CMPENR_MPUEN BIT(0) + +/* DDR Controller registers offsets */ +#define DDRCTRL_STAT 0x004 +#define DDRCTRL_PWRCTL 0x030 +#define DDRCTRL_PWRTMG 0x034 +#define DDRCTRL_HWLPCTL 0x038 +#define DDRCTRL_DFIMISC 0x1b0 +#define DDRCTRL_SWCTL 0x320 +#define DDRCTRL_SWSTAT 0x324 +#define DDRCTRL_PSTAT 0x3fc +#define DDRCTRL_PCTRL_0 0x490 +#define DDRCTRL_PCTRL_1 0x540 + +/* DDR Controller Register fields */ +#define DDRCTRL_STAT_OPERATING_MODE_MASK GENMASK(2, 0) +#define DDRCTRL_STAT_OPERATING_MODE_NORMAL 0x1 +#define DDRCTRL_STAT_OPERATING_MODE_SR 0x3 +#define DDRCTRL_STAT_SELFREF_TYPE_MASK GENMASK(5, 4) +#define DDRCTRL_STAT_SELFREF_TYPE_ASR (0x3 << 4) +#define DDRCTRL_STAT_SELFREF_TYPE_SR (0x2 << 4) + +#define DDRCTRL_PWRCTL_SELFREF_EN BIT(0) +#define DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE BIT(3) +#define DDRCTRL_PWRCTL_SELFREF_SW BIT(5) + +#define DDRCTRL_PWRTMG_SELFREF_TO_X32_MASK GENMASK(23, 16) +#define DDRCTRL_PWRTMG_SELFREF_TO_X32_0 BIT(16) + +#define DDRCTRL_HWLPCTL_HW_LP_EN BIT(0) + +#define DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN BIT(0) + +#define DDRCTRL_SWCTL_SW_DONE BIT(0) + +#define DDRCTRL_SWSTAT_SW_DONE_ACK BIT(0) + +#define DDRCTRL_PSTAT_RD_PORT_BUSY_0 BIT(0) +#define DDRCTRL_PSTAT_RD_PORT_BUSY_1 BIT(1) +#define DDRCTRL_PSTAT_WR_PORT_BUSY_0 BIT(16) +#define DDRCTRL_PSTAT_WR_PORT_BUSY_1 BIT(17) + +#define DDRCTRL_PCTRL_N_PORT_EN BIT(0) + +/* DDR PHY registers offsets */ +#define DDRPHYC_PIR 0x004 +#define DDRPHYC_PGSR 0x00c +#define DDRPHYC_ACDLLCR 0x014 +#define DDRPHYC_ACIOCR 0x024 +#define DDRPHYC_DXCCR 0x028 +#define DDRPHYC_DSGCR 0x02c +#define DDRPHYC_ZQ0CR0 0x180 +#define DDRPHYC_DX0DLLCR 0x1cc +#define DDRPHYC_DX1DLLCR 0x20c +#define DDRPHYC_DX2DLLCR 0x24c +#define DDRPHYC_DX3DLLCR 0x28c + +/* DDR PHY Register fields */ +#define DDRPHYC_PIR_INIT BIT(0) +#define DDRPHYC_PIR_DLLSRST BIT(1) +#define DDRPHYC_PIR_DLLLOCK BIT(2) +#define DDRPHYC_PIR_ITMSRST BIT(4) + +#define DDRPHYC_PGSR_IDONE BIT(0) + +#define DDRPHYC_ACDLLCR_DLLSRST BIT(30) +#define DDRPHYC_ACDLLCR_DLLDIS BIT(31) + +#define DDRPHYC_ACIOCR_ACOE BIT(1) +#define DDRPHYC_ACIOCR_ACPDD BIT(3) +#define DDRPHYC_ACIOCR_ACPDR BIT(4) +#define DDRPHYC_ACIOCR_CKPDD_MASK GENMASK(10, 8) +#define DDRPHYC_ACIOCR_CKPDD_0 BIT(8) +#define DDRPHYC_ACIOCR_CKPDR_MASK GENMASK(13, 11) +#define DDRPHYC_ACIOCR_CKPDR_0 BIT(11) +#define DDRPHYC_ACIOCR_CSPDD_MASK GENMASK(20, 18) +#define DDRPHYC_ACIOCR_CSPDD_0 BIT(18) + +#define DDRPHYC_DXCCR_DXPDD BIT(2) +#define DDRPHYC_DXCCR_DXPDR BIT(3) + +#define DDRPHYC_DSGCR_CKEPDD_MASK GENMASK(19, 16) +#define DDRPHYC_DSGCR_CKEPDD_0 BIT(16) +#define DDRPHYC_DSGCR_ODTPDD_MASK GENMASK(23, 20) +#define DDRPHYC_DSGCR_ODTPDD_0 BIT(20) +#define DDRPHYC_DSGCR_NL2PD BIT(24) +#define DDRPHYC_DSGCR_CKOE BIT(28) + +#define DDRPHYC_ZQ0CRN_ZQPD BIT(31) + +#define DDRPHYC_DXNDLLCR_DLLDIS BIT(31) + +#define BOOT_API_A7_CORE0_MAGIC_NUMBER 0xca7face0 +#define BOOT_API_A7_CORE1_MAGIC_NUMBER 0xca7face1 + +#define MPIDR_AFF0 GENMASK(7, 0) + +#define RCC_MP_GRSTCSETR (STM32_RCC_BASE + 0x0404) +#define RCC_MP_GRSTCSETR_MPSYSRST BIT(0) +#define RCC_MP_GRSTCSETR_MPUP0RST BIT(4) +#define RCC_MP_GRSTCSETR_MPUP1RST BIT(5) + +#define STM32MP1_PSCI_NR_CPUS 2 #if STM32MP1_PSCI_NR_CPUS > CONFIG_ARMV7_PSCI_NR_CPUS #error "invalid value for CONFIG_ARMV7_PSCI_NR_CPUS" #endif @@ -98,6 +231,7 @@ s32 __secure psci_features(u32 function_id, u32 psci_fid) case ARM_PSCI_0_2_FN_MIGRATE_INFO_TYPE: case ARM_PSCI_0_2_FN_SYSTEM_OFF: case ARM_PSCI_0_2_FN_SYSTEM_RESET: + case ARM_PSCI_1_0_FN_SYSTEM_SUSPEND: return 0x0; } return ARM_PSCI_RET_NI; @@ -222,3 +356,374 @@ void __secure psci_system_off(void) while (1) wfi(); } + +static void __secure secure_udelay(unsigned int delay) +{ + u32 freq = cp15_read_cntfrq() / 1000000; + u64 start, end; + + delay *= freq; + + asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (start)); + for (;;) { + asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (end)); + if ((end - start) > delay) + break; + } +} + +static int __secure secure_waitbits(u32 reg, u32 mask, u32 val) +{ + u32 freq = cp15_read_cntfrq() / 1000000; + u32 delay = 500 * freq; /* 500 us */ + u64 start, end; + u32 tmp; + + asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (start)); + for (;;) { + tmp = readl(reg); + tmp &= mask; + if ((tmp & val) == val) + return 0; + asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (end)); + if ((end - start) > delay) + return -ETIMEDOUT; + } +} + +static void __secure ddr_sr_mode_ssr(u32 *saved_pwrctl) +{ + setbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, + RCC_DDRITFCR_DDRC1LPEN | RCC_DDRITFCR_DDRC1EN | + RCC_DDRITFCR_DDRC2LPEN | RCC_DDRITFCR_DDRC2EN | + RCC_DDRITFCR_DDRCAPBLPEN | RCC_DDRITFCR_DDRPHYCAPBLPEN | + RCC_DDRITFCR_DDRCAPBEN | RCC_DDRITFCR_DDRPHYCAPBEN | + RCC_DDRITFCR_DDRPHYCEN); + + clrbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, + RCC_DDRITFCR_AXIDCGEN | RCC_DDRITFCR_DDRCKMOD_MASK); + + /* Disable HW LP interface of uMCTL2 */ + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_HWLPCTL, + DDRCTRL_HWLPCTL_HW_LP_EN); + + /* Configure Automatic LP modes of uMCTL2 */ + clrsetbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRTMG, + DDRCTRL_PWRTMG_SELFREF_TO_X32_MASK, + DDRCTRL_PWRTMG_SELFREF_TO_X32_0); + + /* Save PWRCTL register to restart ASR after suspend (if applicable) */ + *saved_pwrctl = readl(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL); + + /* + * Disable Clock disable with LP modes + * (used in RUN mode for LPDDR2 with specific timing). + */ + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL, + DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE); + + /* Disable automatic Self-Refresh mode */ + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL, + DDRCTRL_PWRCTL_SELFREF_EN); +} + +static void __secure ddr_sr_mode_restore(u32 saved_pwrctl) +{ + saved_pwrctl &= DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE | + DDRCTRL_PWRCTL_SELFREF_EN; + + /* Restore ASR mode in case it was enabled before suspend. */ + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL, saved_pwrctl); +} + +static int __secure ddr_sw_self_refresh_in(void) +{ + int ret; + + clrbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN); + + /* Blocks AXI ports from taking anymore transactions */ + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PCTRL_0, + DDRCTRL_PCTRL_N_PORT_EN); + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PCTRL_1, + DDRCTRL_PCTRL_N_PORT_EN); + + /* + * Waits unit all AXI ports are idle + * Poll PSTAT.rd_port_busy_n = 0 + * Poll PSTAT.wr_port_busy_n = 0 + */ + ret = secure_waitbits(STM32_DDRCTRL_BASE + DDRCTRL_PSTAT, + DDRCTRL_PSTAT_RD_PORT_BUSY_0 | + DDRCTRL_PSTAT_RD_PORT_BUSY_1 | + DDRCTRL_PSTAT_WR_PORT_BUSY_0 | + DDRCTRL_PSTAT_WR_PORT_BUSY_1, 0); + if (ret) + goto pstat_failed; + + /* SW Self-Refresh entry */ + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL, DDRCTRL_PWRCTL_SELFREF_SW); + + /* + * Wait operating mode change in self-refresh mode + * with STAT.operating_mode[1:0]==11. + * Ensure transition to self-refresh was due to software + * by checking also that STAT.selfref_type[1:0]=2. + */ + ret = secure_waitbits(STM32_DDRCTRL_BASE + DDRCTRL_STAT, + DDRCTRL_STAT_OPERATING_MODE_MASK | + DDRCTRL_STAT_SELFREF_TYPE_MASK, + DDRCTRL_STAT_OPERATING_MODE_SR | + DDRCTRL_STAT_SELFREF_TYPE_SR); + if (ret) + goto selfref_sw_failed; + + /* IOs powering down (PUBL registers) */ + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACPDD); + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACPDR); + + clrsetbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, + DDRPHYC_ACIOCR_CKPDD_MASK, + DDRPHYC_ACIOCR_CKPDD_0); + + clrsetbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, + DDRPHYC_ACIOCR_CKPDR_MASK, + DDRPHYC_ACIOCR_CKPDR_0); + + clrsetbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, + DDRPHYC_ACIOCR_CSPDD_MASK, + DDRPHYC_ACIOCR_CSPDD_0); + + /* Disable command/address output driver */ + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACOE); + + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDD); + + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDR); + + clrsetbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, + DDRPHYC_DSGCR_ODTPDD_MASK, + DDRPHYC_DSGCR_ODTPDD_0); + + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, DDRPHYC_DSGCR_NL2PD); + + clrsetbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, + DDRPHYC_DSGCR_CKEPDD_MASK, + DDRPHYC_DSGCR_CKEPDD_0); + + /* Disable PZQ cell (PUBL register) */ + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ZQ0CR0, DDRPHYC_ZQ0CRN_ZQPD); + + /* Set latch */ + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, DDRPHYC_DSGCR_CKOE); + + /* Additional delay to avoid early latch */ + secure_udelay(10); + + /* Activate sw retention in PWRCTRL */ + setbits_le32(STM32_PWR_BASE + PWR_CR3, PWR_CR3_DDRRETEN); + + /* Switch controller clocks (uMCTL2/PUBL) to DLL ref clock */ + setbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); + + /* Disable all DLLs: GLITCH window */ + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACDLLCR, DDRPHYC_ACDLLCR_DLLDIS); + + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX0DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); + + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX1DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); + + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX2DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); + + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX3DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); + + /* Switch controller clocks (uMCTL2/PUBL) to DLL output clock */ + clrbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); + + /* Deactivate all DDR clocks */ + clrbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, + RCC_DDRITFCR_DDRC1EN | RCC_DDRITFCR_DDRC2EN | + RCC_DDRITFCR_DDRCAPBEN | RCC_DDRITFCR_DDRPHYCAPBEN); + + return 0; + +selfref_sw_failed: + /* This bit should be cleared to restore DDR in its previous state */ + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL, + DDRCTRL_PWRCTL_SELFREF_SW); + +pstat_failed: + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PCTRL_0, + DDRCTRL_PCTRL_N_PORT_EN); + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PCTRL_1, + DDRCTRL_PCTRL_N_PORT_EN); + + return -EINVAL; +}; + +static void __secure ddr_sw_self_refresh_exit(void) +{ + int ret; + + /* Enable all clocks */ + setbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, + RCC_DDRITFCR_DDRC1EN | RCC_DDRITFCR_DDRC2EN | + RCC_DDRITFCR_DDRPHYCEN | RCC_DDRITFCR_DDRPHYCAPBEN | + RCC_DDRITFCR_DDRCAPBEN); + + /* Handshake */ + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_SWCTL, DDRCTRL_SWCTL_SW_DONE); + + /* Mask dfi_init_complete_en */ + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_DFIMISC, + DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + + /* Ack */ + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_SWCTL, DDRCTRL_SWCTL_SW_DONE); + ret = secure_waitbits(STM32_DDRCTRL_BASE + DDRCTRL_SWSTAT, + DDRCTRL_SWSTAT_SW_DONE_ACK, + DDRCTRL_SWSTAT_SW_DONE_ACK); + if (ret) + hang(); + + /* Switch controller clocks (uMCTL2/PUBL) to DLL ref clock */ + setbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); + + /* Enable all DLLs: GLITCH window */ + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACDLLCR, + DDRPHYC_ACDLLCR_DLLDIS); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX0DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX1DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX2DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DX3DLLCR, DDRPHYC_DXNDLLCR_DLLDIS); + + /* Additional delay to avoid early DLL clock switch */ + secure_udelay(50); + + /* Switch controller clocks (uMCTL2/PUBL) to DLL ref clock */ + clrbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACDLLCR, DDRPHYC_ACDLLCR_DLLSRST); + + secure_udelay(10); + + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACDLLCR, DDRPHYC_ACDLLCR_DLLSRST); + + /* PHY partial init: (DLL lock and ITM reset) */ + writel(DDRPHYC_PIR_DLLSRST | DDRPHYC_PIR_DLLLOCK | + DDRPHYC_PIR_ITMSRST | DDRPHYC_PIR_INIT, + STM32_DDRPHYC_BASE + DDRPHYC_PIR); + + /* Need to wait at least 10 clock cycles before accessing PGSR */ + secure_udelay(1); + + /* Pool end of init */ + ret = secure_waitbits(STM32_DDRPHYC_BASE + DDRPHYC_PGSR, + DDRPHYC_PGSR_IDONE, DDRPHYC_PGSR_IDONE); + if (ret) + hang(); + + /* Handshake */ + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_SWCTL, DDRCTRL_SWCTL_SW_DONE); + + /* Unmask dfi_init_complete_en to uMCTL2 */ + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_DFIMISC, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + + /* Ack */ + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_SWCTL, DDRCTRL_SWCTL_SW_DONE); + ret = secure_waitbits(STM32_DDRCTRL_BASE + DDRCTRL_SWSTAT, + DDRCTRL_SWSTAT_SW_DONE_ACK, + DDRCTRL_SWSTAT_SW_DONE_ACK); + if (ret) + hang(); + + /* Deactivate sw retention in PWR */ + clrbits_le32(STM32_PWR_BASE + PWR_CR3, PWR_CR3_DDRRETEN); + + /* Enable PZQ cell (PUBL register) */ + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ZQ0CR0, DDRPHYC_ZQ0CRN_ZQPD); + + /* Enable pad drivers */ + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACPDD); + + /* Enable command/address output driver */ + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACOE); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_CKPDD_MASK); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_CSPDD_MASK); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDD); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDR); + + /* Release latch */ + setbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, DDRPHYC_DSGCR_CKOE); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, DDRPHYC_DSGCR_ODTPDD_MASK); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, DDRPHYC_DSGCR_NL2PD); + + clrbits_le32(STM32_DDRPHYC_BASE + DDRPHYC_DSGCR, DDRPHYC_DSGCR_CKEPDD_MASK); + + /* Remove selfrefresh */ + clrbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PWRCTL, DDRCTRL_PWRCTL_SELFREF_SW); + + /* Wait operating_mode == normal */ + ret = secure_waitbits(STM32_DDRCTRL_BASE + DDRCTRL_STAT, + DDRCTRL_STAT_OPERATING_MODE_MASK, + DDRCTRL_STAT_OPERATING_MODE_NORMAL); + if (ret) + hang(); + + /* AXI ports are no longer blocked from taking transactions */ + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PCTRL_0, DDRCTRL_PCTRL_N_PORT_EN); + setbits_le32(STM32_DDRCTRL_BASE + DDRCTRL_PCTRL_1, DDRCTRL_PCTRL_N_PORT_EN); + + setbits_le32(STM32_RCC_BASE + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN); +} + +void __secure psci_system_suspend(u32 __always_unused function_id, + u32 ep, u32 context_id) +{ + u32 saved_pwrctl, reg; + + /* Disable IO compensation */ + + /* Place current APSRC/ANSRC into RAPSRC/RANSRC */ + reg = readl(STM32_SYSCFG_BASE + SYSCFG_CMPCR); + reg >>= 8; + reg &= 0xff << 16; + reg |= SYSCFG_CMPCR_SW_CTRL; + writel(reg, STM32_SYSCFG_BASE + SYSCFG_CMPCR); + writel(SYSCFG_CMPENR_MPUEN, STM32_SYSCFG_BASE + SYSCFG_CMPENCLRR); + + writel(RCC_MP_CIFR_WKUPF, STM32_RCC_BASE + RCC_MP_CIFR); + setbits_le32(STM32_RCC_BASE + RCC_MP_CIER, RCC_MP_CIFR_WKUPF); + + setbits_le32(STM32_PWR_BASE + PWR_MPUCR, + PWR_MPUCR_CSSF | PWR_MPUCR_CSTDBYDIS | PWR_MPUCR_PDDS); + + psci_v7_flush_dcache_all(); + ddr_sr_mode_ssr(&saved_pwrctl); + ddr_sw_self_refresh_in(); + setbits_le32(STM32_PWR_BASE + PWR_CR3, PWR_CR3_DDRSREN); + writel(0x3, STM32_RCC_BASE + RCC_MP_SREQSETR); + + /* Zzz, enter stop mode */ + asm volatile( + "isb\n" + "dsb\n" + "wfi\n"); + + writel(0x3, STM32_RCC_BASE + RCC_MP_SREQCLRR); + ddr_sw_self_refresh_exit(); + ddr_sr_mode_restore(saved_pwrctl); + + writel(SYSCFG_CMPENR_MPUEN, STM32_SYSCFG_BASE + SYSCFG_CMPENSETR); + clrbits_le32(STM32_SYSCFG_BASE + SYSCFG_CMPCR, SYSCFG_CMPCR_SW_CTRL); +} -- cgit v1.2.3 From 19fbe102b2e21db949a66d9020da097769bbdb3c Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 14 Mar 2022 13:35:54 +0100 Subject: ARM: dts: stm32: Add DFU support for DHCOM recovery This patch configures U-Boot SPL for DHCOM SoM to permit DFU upload of SPL and subsequent u-boot.itb for recovery or commissioning purposes. The DFU usage procedure is identical to STM32MP1 DHCOR SoM, see commit 3919aa1722a ("ARM: dts: stm32: Add DFU support for DHCOR recovery") , except for switching the SoM into DFU mode. By default, the DHCOM SoM has no dedicated mechanism for setting BOOTn straps into UART/USB mode, therefore to enter DFU mode, the SoC must fail to boot from boot media which can be selected by the BOOTn strap override mechanism first and then fall back to DFU mode. In case of a SoM with pre-populated BOOTn strap override button, power the system off, remove microSD card (if applicable), hold down the BOOTn strap override button located between eMMC and SoM edge connector, power on the SoM. The SoC will fail to boot from SD card and fall back into DFU mode. Signed-off-by: Marek Vasut Cc: Patrice Chotard Cc: Patrick Delaunay Reviewed-by: Patrice Chotard --- arch/arm/dts/stm32mp15xx-dhcom-u-boot.dtsi | 42 ++++++++++++++++++++++++++++++ configs/stm32mp15_dhcom_basic_defconfig | 15 ++++++++--- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/arch/arm/dts/stm32mp15xx-dhcom-u-boot.dtsi b/arch/arm/dts/stm32mp15xx-dhcom-u-boot.dtsi index f09f4290f6..d73967ac1b 100644 --- a/arch/arm/dts/stm32mp15xx-dhcom-u-boot.dtsi +++ b/arch/arm/dts/stm32mp15xx-dhcom-u-boot.dtsi @@ -58,6 +58,7 @@ &i2c4 { u-boot,dm-pre-reloc; + u-boot,dm-spl; eeprom0: eeprom@50 { }; @@ -98,6 +99,11 @@ &pmic { u-boot,dm-pre-reloc; + u-boot,dm-spl; + + regulators { + u-boot,dm-spl; + }; }; &flash0 { @@ -288,3 +294,39 @@ bias-pull-up; }; }; + +®11 { + u-boot,dm-spl; +}; + +®18 { + u-boot,dm-spl; +}; + +&usb33 { + u-boot,dm-spl; +}; + +&usbotg_hs_pins_a { + u-boot,dm-spl; +}; + +&usbotg_hs { + u-boot,dm-spl; +}; + +&usbphyc { + u-boot,dm-spl; +}; + +&usbphyc_port0 { + u-boot,dm-spl; +}; + +&usbphyc_port1 { + u-boot,dm-spl; +}; + +&vdd_usb { + u-boot,dm-spl; +}; diff --git a/configs/stm32mp15_dhcom_basic_defconfig b/configs/stm32mp15_dhcom_basic_defconfig index c422c47775..438bba37de 100644 --- a/configs/stm32mp15_dhcom_basic_defconfig +++ b/configs/stm32mp15_dhcom_basic_defconfig @@ -18,6 +18,7 @@ CONFIG_DISTRO_DEFAULTS=y CONFIG_SYS_LOAD_ADDR=0xc2000000 CONFIG_FIT=y CONFIG_SPL_LOAD_FIT=y +CONFIG_SPL_LOAD_FIT_ADDRESS=0xc1000000 CONFIG_SPL_FIT_SOURCE="board/dhelectronics/dh_stm32mp1/u-boot-dhcom.its" # CONFIG_USE_SPL_FIT_GENERATOR is not set CONFIG_BOOTDELAY=1 @@ -27,12 +28,17 @@ CONFIG_BOARD_EARLY_INIT_F=y CONFIG_SPL_LEGACY_IMAGE_SUPPORT=y CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_PARTITION=y CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_PARTITION=3 +CONFIG_SPL_ENV_SUPPORT=y CONFIG_SPL_I2C=y CONFIG_SPL_MTD_SUPPORT=y CONFIG_SPL_DM_SPI_FLASH=y CONFIG_SPL_POWER=y +CONFIG_SPL_RAM_SUPPORT=y +CONFIG_SPL_RAM_DEVICE=y CONFIG_SPL_SPI_FLASH_MTD=y CONFIG_SYS_SPI_U_BOOT_OFFS=0x80000 +CONFIG_SPL_USB_GADGET=y +CONFIG_SPL_DFU=y CONFIG_SYS_PROMPT="STM32MP> " # CONFIG_CMD_ELF is not set # CONFIG_CMD_EXPORTENV is not set @@ -71,6 +77,7 @@ CONFIG_OF_SPL_REMOVE_PROPS="interrupts interrupt-names interrupts-extended inter CONFIG_ENV_IS_IN_SPI_FLASH=y CONFIG_SYS_REDUNDAND_ENVIRONMENT=y CONFIG_SYS_RELOC_GD_ENV_ADDR=y +CONFIG_SPL_ENV_IS_NOWHERE=y CONFIG_NET_RANDOM_ETHADDR=y CONFIG_IP_DEFRAG=y CONFIG_TFTP_BLOCKSIZE=1536 @@ -79,8 +86,6 @@ CONFIG_SPL_BLOCK_CACHE=y CONFIG_DFU_MMC=y CONFIG_DFU_MTD=y CONFIG_DFU_RAM=y -CONFIG_DFU_VIRT=y -CONFIG_SET_DFU_ALT_INFO=y CONFIG_GPIO_HOG=y CONFIG_DM_HWSPINLOCK=y CONFIG_HWSPINLOCK_STM32=y @@ -106,18 +111,20 @@ CONFIG_DM_ETH=y CONFIG_DWC_ETH_QOS=y CONFIG_KS8851_MLL=y CONFIG_PHY=y +CONFIG_SPL_PHY=y CONFIG_PHY_STM32_USBPHYC=y CONFIG_PINCONF=y # CONFIG_SPL_PINCTRL_FULL is not set CONFIG_PINCTRL_STMFX=y CONFIG_DM_PMIC=y -# CONFIG_SPL_PMIC_CHILDREN is not set CONFIG_PMIC_STPMIC1=y CONFIG_DM_REGULATOR=y +CONFIG_SPL_DM_REGULATOR=y CONFIG_DM_REGULATOR_FIXED=y CONFIG_DM_REGULATOR_GPIO=y CONFIG_DM_REGULATOR_STM32_VREFBUF=y CONFIG_DM_REGULATOR_STPMIC1=y +CONFIG_SPL_DM_REGULATOR_STPMIC1=y CONFIG_REMOTEPROC_STM32_COPRO=y CONFIG_DM_RTC=y CONFIG_RTC_STM32=y @@ -129,8 +136,10 @@ CONFIG_STM32_SPI=y CONFIG_SYSRESET_SYSCON=y CONFIG_USB=y CONFIG_DM_USB_GADGET=y +CONFIG_SPL_DM_USB_GADGET=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_EHCI_GENERIC=y +CONFIG_USB_DWC2=y CONFIG_USB_HOST_ETHER=y CONFIG_USB_ETHER_ASIX=y CONFIG_USB_GADGET=y -- cgit v1.2.3 From fd426b31066ba61ee1ff96a2b56c919251ffdd9e Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Tue, 1 Mar 2022 10:35:39 +0000 Subject: k210: use the board vendor name rather than the marketing name "kendryte" is the marketing name for the K210 RISC-V SoC produced by Canaan Inc. Rather than "kendryte,k210", use the usual "canaan,k210" vendor,SoC compatibility string format in the device tree files and use the SoC name for file names. With these changes, the device tree files are more in sync with the Linux kernel DTS and drivers, making uboot device tree usable by the kernel. Signed-off-by: Damien Le Moal Signed-off-by: Niklas Cassel Reviewed-by: Leo Yu-Chi Liang --- MAINTAINERS | 12 +- arch/riscv/dts/k210-maix-bit.dts | 3 +- arch/riscv/dts/k210.dtsi | 98 +- board/sipeed/maix/maix.c | 2 +- doc/board/sipeed/maix.rst | 20 +- .../mfd/canaan,k210-sysctl.txt | 33 + .../mfd/kendryte,k210-sysctl.txt | 33 - .../pinctrl/canaan,k210-fpioa.txt | 102 ++ .../pinctrl/kendryte,k210-fpioa.txt | 102 -- doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt | 4 +- drivers/clk/Makefile | 2 +- drivers/clk/clk_k210.c | 1344 ++++++++++++++++++++ drivers/clk/clk_kendryte.c | 1344 -------------------- drivers/pinctrl/Makefile | 2 +- drivers/pinctrl/pinctrl-k210.c | 740 +++++++++++ drivers/pinctrl/pinctrl-kendryte.c | 740 ----------- drivers/spi/designware_spi.c | 4 +- include/configs/sipeed-maix.h | 2 +- include/k210/pll.h | 24 + include/kendryte/pll.h | 24 - test/dm/k210_pll.c | 2 +- 21 files changed, 2319 insertions(+), 2318 deletions(-) create mode 100644 doc/device-tree-bindings/mfd/canaan,k210-sysctl.txt delete mode 100644 doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt create mode 100644 doc/device-tree-bindings/pinctrl/canaan,k210-fpioa.txt delete mode 100644 doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt create mode 100644 drivers/clk/clk_k210.c delete mode 100644 drivers/clk/clk_kendryte.c create mode 100644 drivers/pinctrl/pinctrl-k210.c delete mode 100644 drivers/pinctrl/pinctrl-kendryte.c create mode 100644 include/k210/pll.h delete mode 100644 include/kendryte/pll.h diff --git a/MAINTAINERS b/MAINTAINERS index 82fc49e31d..8defd09a64 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1115,14 +1115,14 @@ F: drivers/timer/andes_plmt_timer.c F: drivers/timer/sifive_clint_timer.c F: tools/prelink-riscv.c -RISC-V KENDRYTE +RISC-V CANAAN KENDRYTE K210 M: Sean Anderson S: Maintained -F: doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt -F: doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt -F: drivers/clk/clk_kendryte.c -F: drivers/pinctrl/pinctrl-kendryte.c -F: include/kendryte/ +F: doc/device-tree-bindings/mfd/canaan,k210-sysctl.txt +F: doc/device-tree-bindings/pinctrl/canaan,k210-fpioa.txt +F: drivers/clk/clk_k210.c +F: drivers/pinctrl/pinctrl-k210.c +F: include/k210/ RNG M: Sughosh Ganu diff --git a/arch/riscv/dts/k210-maix-bit.dts b/arch/riscv/dts/k210-maix-bit.dts index 902dcfd08a..c4bbf6b018 100644 --- a/arch/riscv/dts/k210-maix-bit.dts +++ b/arch/riscv/dts/k210-maix-bit.dts @@ -12,7 +12,8 @@ / { model = "Sipeed Maix Bit 2.0"; - compatible = "sipeed,maix-bitm", "sipeed,maix-bit", "kendryte,k210"; + compatible = "sipeed,maix-bitm", "sipeed,maix-bit", + "canaan,kendryte-k210"; chosen { stdout-path = "serial0:115200"; diff --git a/arch/riscv/dts/k210.dtsi b/arch/riscv/dts/k210.dtsi index 8bcd3cebde..7dc2785a3e 100644 --- a/arch/riscv/dts/k210.dtsi +++ b/arch/riscv/dts/k210.dtsi @@ -15,7 +15,7 @@ */ #address-cells = <1>; #size-cells = <1>; - compatible = "kendryte,k210"; + compatible = "canaan,kendryte-k210"; aliases { cpu0 = &cpu0; @@ -46,7 +46,7 @@ timebase-frequency = <7800000>; cpu0: cpu@0 { device_type = "cpu"; - compatible = "kendryte,k210", "sifive,rocket0", "riscv"; + compatible = "canaan,k210", "sifive,rocket0", "riscv"; reg = <0>; riscv,isa = "rv64imafdgc"; mmu-type = "sv39"; @@ -63,7 +63,7 @@ }; cpu1: cpu@1 { device_type = "cpu"; - compatible = "kendryte,k210", "sifive,rocket0", "riscv"; + compatible = "canaan,k210", "sifive,rocket0", "riscv"; reg = <1>; riscv,isa = "rv64imafdgc"; mmu-type = "sv39"; @@ -82,7 +82,7 @@ sram: memory@80000000 { device_type = "memory"; - compatible = "kendryte,k210-sram"; + compatible = "canaan,k210-sram"; reg = <0x80000000 0x400000>, <0x80400000 0x200000>, <0x80600000 0x200000>; @@ -106,12 +106,12 @@ soc { #address-cells = <1>; #size-cells = <1>; - compatible = "kendryte,k210-soc", "simple-bus"; + compatible = "canaan,k210-soc", "simple-bus"; ranges; interrupt-parent = <&plic0>; debug0: debug@0 { - compatible = "kendryte,k210-debug", "riscv,debug"; + compatible = "canaan,k210-debug", "riscv,debug"; reg = <0x0 0x1000>; }; @@ -122,7 +122,7 @@ clint0: clint@2000000 { #interrupt-cells = <1>; - compatible = "kendryte,k210-clint", "riscv,clint0"; + compatible = "canaan,k210-clint", "sifive,clint0", "riscv,clint0"; reg = <0x2000000 0xC000>; interrupts-extended = <&cpu0_intc 3>, <&cpu0_intc 7>, <&cpu1_intc 3>, <&cpu1_intc 7>; @@ -131,7 +131,7 @@ plic0: interrupt-controller@C000000 { #interrupt-cells = <1>; - compatible = "kendryte,k210-plic", "riscv,plic0"; + compatible = "canaan,k210-plic", "sifive,plic-1.0.0", "riscv,plic0"; reg = <0xC000000 0x4000000>; interrupt-controller; interrupts-extended = <&cpu0_intc 9>, <&cpu0_intc 11>, @@ -141,7 +141,7 @@ }; uarths0: serial@38000000 { - compatible = "kendryte,k210-uarths", "sifive,uart0"; + compatible = "canaan,k210-uarths", "sifive,uart0"; reg = <0x38000000 0x1000>; interrupts = <33>; clocks = <&sysclk K210_CLK_CPU>; @@ -151,7 +151,7 @@ gpio0: gpio-controller@38001000 { #interrupt-cells = <2>; #gpio-cells = <2>; - compatible = "kendryte,k210-gpiohs", "sifive,gpio0"; + compatible = "canaan,k210-gpiohs", "sifive,gpio0"; reg = <0x38001000 0x1000>; interrupt-controller; interrupts = <34 35 36 37 38 39 40 41 @@ -164,7 +164,7 @@ }; kpu0: kpu@40800000 { - compatible = "kendryte,k210-kpu"; + compatible = "canaan,k210-kpu"; reg = <0x40800000 0xc00000>; interrupts = <25>; clocks = <&sysclk K210_CLK_AI>; @@ -172,7 +172,7 @@ }; fft0: fft@42000000 { - compatible = "kendryte,k210-fft"; + compatible = "canaan,k210-fft"; reg = <0x42000000 0x400000>; interrupts = <26>; clocks = <&sysclk K210_CLK_FFT>; @@ -181,7 +181,7 @@ }; dmac0: dma-controller@50000000 { - compatible = "kendryte,k210-dmac", "snps,axi-dma-1.01a"; + compatible = "canaan,k210-dmac", "snps,axi-dma-1.01a"; reg = <0x50000000 0x1000>; interrupts = <27 28 29 30 31 32>; clocks = <&sysclk K210_CLK_DMA>, <&sysclk K210_CLK_DMA>; @@ -199,14 +199,14 @@ apb0: bus@50200000 { #address-cells = <1>; #size-cells = <1>; - compatible = "kendryte,k210-apb", "simple-pm-bus"; + compatible = "canaan,k210-apb", "simple-pm-bus"; ranges; clocks = <&sysclk K210_CLK_APB0>; gpio1: gpio-controller@50200000 { #address-cells = <1>; #size-cells = <0>; - compatible = "kendryte,k210-gpio", + compatible = "canaan,k210-gpio", "snps,dw-apb-gpio"; reg = <0x50200000 0x80>; clocks = <&sysclk K210_CLK_GPIO>; @@ -226,7 +226,7 @@ }; uart1: serial@50210000 { - compatible = "kendryte,k210-uart", + compatible = "canaan,k210-uart", "snps,dw-apb-uart"; reg = <0x50210000 0x100>; interrupts = <11>; @@ -242,7 +242,7 @@ }; uart2: serial@50220000 { - compatible = "kendryte,k210-uart", + compatible = "canaan,k210-uart", "snps,dw-apb-uart"; reg = <0x50220000 0x100>; interrupts = <12>; @@ -258,7 +258,7 @@ }; uart3: serial@50230000 { - compatible = "kendryte,k210-uart", + compatible = "canaan,k210-uart", "snps,dw-apb-uart"; reg = <0x50230000 0x100>; interrupts = <13>; @@ -274,7 +274,7 @@ }; spi2: spi@50240000 { - compatible = "canaan,kendryte-k210-spi", + compatible = "canaan,k210-spi", "snps,dw-apb-ssi-4.01", "snps,dw-apb-ssi"; spi-slave; @@ -287,7 +287,7 @@ }; i2s0: i2s@50250000 { - compatible = "kendryte,k210-i2s", + compatible = "canaan,k210-i2s", "snps,designware-i2s"; reg = <0x50250000 0x200>; interrupts = <5>; @@ -298,13 +298,13 @@ }; apu0: sound@520250200 { - compatible = "kendryte,k210-apu"; + compatible = "canaan,k210-apu"; reg = <0x50250200 0x200>; status = "disabled"; }; i2s1: i2s@50260000 { - compatible = "kendryte,k210-i2s", + compatible = "canaan,k210-i2s", "snps,designware-i2s"; reg = <0x50260000 0x200>; interrupts = <6>; @@ -315,7 +315,7 @@ }; i2s2: i2s@50270000 { - compatible = "kendryte,k210-i2s", + compatible = "canaan,k210-i2s", "snps,designware-i2s"; reg = <0x50270000 0x200>; interrupts = <7>; @@ -326,7 +326,7 @@ }; i2c0: i2c@50280000 { - compatible = "kendryte,k210-i2c", + compatible = "canaan,k210-i2c", "snps,designware-i2c"; reg = <0x50280000 0x100>; interrupts = <8>; @@ -336,7 +336,7 @@ }; i2c1: i2c@50290000 { - compatible = "kendryte,k210-i2c", + compatible = "canaan,k210-i2c", "snps,designware-i2c"; reg = <0x50290000 0x100>; interrupts = <9>; @@ -346,7 +346,7 @@ }; i2c2: i2c@502A0000 { - compatible = "kendryte,k210-i2c", + compatible = "canaan,k210-i2c", "snps,designware-i2c"; reg = <0x502A0000 0x100>; interrupts = <10>; @@ -356,12 +356,12 @@ }; fpioa: pinmux@502B0000 { - compatible = "kendryte,k210-fpioa"; + compatible = "canaan,k210-fpioa"; reg = <0x502B0000 0x100>; clocks = <&sysclk K210_CLK_FPIOA>; resets = <&sysrst K210_RST_FPIOA>; - kendryte,sysctl = <&sysctl>; - kendryte,power-offset = ; + canaan,k210-sysctl = <&sysctl>; + canaan,k210-power-offset = ; pinctrl-0 = <&fpioa_jtag>; pinctrl-names = "default"; status = "disabled"; @@ -375,7 +375,7 @@ }; sha256: sha256@502C0000 { - compatible = "kendryte,k210-sha256"; + compatible = "canaan,k210-sha256"; reg = <0x502C0000 0x100>; clocks = <&sysclk K210_CLK_SHA>; resets = <&sysrst K210_RST_SHA>; @@ -383,7 +383,7 @@ }; timer0: timer@502D0000 { - compatible = "kendryte,k210-timer", + compatible = "canaan,k210-timer", "snps,dw-apb-timer"; reg = <0x502D0000 0x100>; interrupts = <14 15>; @@ -394,7 +394,7 @@ }; timer1: timer@502E0000 { - compatible = "kendryte,k210-timer", + compatible = "canaan,k210-timer", "snps,dw-apb-timer"; reg = <0x502E0000 0x100>; interrupts = <16 17>; @@ -405,7 +405,7 @@ }; timer2: timer@502F0000 { - compatible = "kendryte,k210-timer", + compatible = "canaan,k210-timer", "snps,dw-apb-timer"; reg = <0x502F0000 0x100>; interrupts = <18 19>; @@ -419,12 +419,12 @@ apb1: bus@50400000 { #address-cells = <1>; #size-cells = <1>; - compatible = "kendryte,k210-apb", "simple-pm-bus"; + compatible = "canaan,k210-apb", "simple-pm-bus"; ranges; clocks = <&sysclk K210_CLK_APB1>; wdt0: watchdog@50400000 { - compatible = "kendryte,k210-wdt", "snps,dw-wdt"; + compatible = "canaan,k210-wdt", "snps,dw-wdt"; reg = <0x50400000 0x100>; interrupts = <21>; clocks = <&sysclk K210_CLK_WDT0>; @@ -432,7 +432,7 @@ }; wdt1: watchdog@50410000 { - compatible = "kendryte,k210-wdt", "snps,dw-wdt"; + compatible = "canaan,k210-wdt", "snps,dw-wdt"; reg = <0x50410000 0x100>; interrupts = <22>; clocks = <&sysclk K210_CLK_WDT1>; @@ -443,7 +443,7 @@ otp0: nvmem@50420000 { #address-cells = <1>; #size-cells = <1>; - compatible = "kendryte,k210-otp"; + compatible = "canaan,k210-otp"; reg = <0x50420000 0x100>, <0x88000000 0x20000>; reg-names = "reg", "mem"; @@ -480,18 +480,18 @@ }; dvp0: camera@50430000 { - compatible = "kendryte,k210-dvp"; + compatible = "canaan,k210-dvp"; reg = <0x50430000 0x100>; interrupts = <24>; clocks = <&sysclk K210_CLK_DVP>; resets = <&sysrst K210_RST_DVP>; - kendryte,sysctl = <&sysctl>; - kendryte,misc-offset = ; + canaan,k210-sysctl = <&sysctl>; + canaan,k210-misc-offset = ; status = "disabled"; }; sysctl: syscon@50440000 { - compatible = "kendryte,k210-sysctl", + compatible = "canaan,k210-sysctl", "syscon", "simple-mfd"; reg = <0x50440000 0x100>; reg-io-width = <4>; @@ -499,7 +499,7 @@ sysclk: clock-controller { #clock-cells = <1>; - compatible = "kendryte,k210-clk"; + compatible = "canaan,k210-clk"; clocks = <&in0>; assigned-clocks = <&sysclk K210_CLK_PLL1>; assigned-clock-rates = <390000000>; @@ -507,7 +507,7 @@ }; sysrst: reset-controller { - compatible = "kendryte,k210-rst", + compatible = "canaan,k210-rst", "syscon-reset"; #reset-cells = <1>; regmap = <&sysctl>; @@ -526,7 +526,7 @@ }; aes0: aes@50450000 { - compatible = "kendryte,k210-aes"; + compatible = "canaan,k210-aes"; reg = <0x50450000 0x100>; clocks = <&sysclk K210_CLK_AES>; resets = <&sysrst K210_RST_AES>; @@ -534,7 +534,7 @@ }; rtc: rtc@50460000 { - compatible = "kendryte,k210-rtc"; + compatible = "canaan,k210-rtc"; reg = <0x50460000 0x100>; clocks = <&in0>; resets = <&sysrst K210_RST_RTC>; @@ -546,14 +546,14 @@ apb2: bus@52000000 { #address-cells = <1>; #size-cells = <1>; - compatible = "kendryte,k210-apb", "simple-pm-bus"; + compatible = "canaan,k210-apb", "simple-pm-bus"; ranges; clocks = <&sysclk K210_CLK_APB2>; spi0: spi@52000000 { #address-cells = <1>; #size-cells = <0>; - compatible = "canaan,kendryte-k210-spi", + compatible = "canaan,k210-spi", "snps,dw-apb-ssi-4.01", "snps,dw-apb-ssi"; reg = <0x52000000 0x100>; @@ -570,7 +570,7 @@ spi1: spi@53000000 { #address-cells = <1>; #size-cells = <0>; - compatible = "canaan,kendryte-k210-spi", + compatible = "canaan,k210-spi", "snps,dw-apb-ssi-4.01", "snps,dw-apb-ssi"; reg = <0x53000000 0x100>; @@ -587,7 +587,7 @@ spi3: spi@54000000 { #address-cells = <1>; #size-cells = <0>; - compatible = "canaan,kendryte-k210-ssi", + compatible = "canaan,k210-ssi", "snps,dwc-ssi-1.01a"; reg = <0x54000000 0x200>; interrupts = <4>; diff --git a/board/sipeed/maix/maix.c b/board/sipeed/maix/maix.c index 52e4fee2f0..a218278cb3 100644 --- a/board/sipeed/maix/maix.c +++ b/board/sipeed/maix/maix.c @@ -22,7 +22,7 @@ static int sram_init(void) struct clk clk; /* Enable RAM clocks */ - memory = ofnode_by_compatible(ofnode_null(), "kendryte,k210-sram"); + memory = ofnode_by_compatible(ofnode_null(), "canaan,k210-sram"); if (ofnode_equal(memory, ofnode_null())) return -ENOENT; diff --git a/doc/board/sipeed/maix.rst b/doc/board/sipeed/maix.rst index ef79297ef0..903f8831d7 100644 --- a/doc/board/sipeed/maix.rst +++ b/doc/board/sipeed/maix.rst @@ -4,16 +4,16 @@ MAIX ==== -Several of the Sipeed Maix series of boards cotain the Kendryte K210 processor, -a 64-bit RISC-V CPU. This processor contains several peripherals to accelerate -neural network processing and other "ai" tasks. This includes a "KPU" neural -network processor, an audio processor supporting beamforming reception, and a -digital video port supporting capture and output at VGA resolution. Other -peripherals include 8M of SRAM (accessible with and without caching); remappable -pins, including 40 GPIOs; AES, FFT, and SHA256 accelerators; a DMA controller; -and I2C, I2S, and SPI controllers. Maix peripherals vary, but include spi flash; -on-board usb-serial bridges; ports for cameras, displays, and sd cards; and -ESP32 chips. +Several of the Sipeed Maix series of boards contain the Kendryte K210 processor, +a 64-bit RISC-V CPU produced by Canaan Inc. This processor contains several +peripherals to accelerate neural network processing and other "ai" tasks. This +includes a "KPU" neural network processor, an audio processor supporting +beamforming reception, and a digital video port supporting capture and output at +VGA resolution. Other peripherals include 8M of SRAM (accessible with and +without caching); remappable pins, including 40 GPIOs; AES, FFT, and SHA256 +accelerators; a DMA controller; and I2C, I2S, and SPI controllers. Maix +peripherals vary, but include spi flash; on-board usb-serial bridges; ports for +cameras, displays, and sd cards; and ESP32 chips. Currently, only the Sipeed MAIX BiT V2.0 (bitm) and Sipeed MAIXDUINO are supported, but the boards are fairly similar. diff --git a/doc/device-tree-bindings/mfd/canaan,k210-sysctl.txt b/doc/device-tree-bindings/mfd/canaan,k210-sysctl.txt new file mode 100644 index 0000000000..e48b164fc0 --- /dev/null +++ b/doc/device-tree-bindings/mfd/canaan,k210-sysctl.txt @@ -0,0 +1,33 @@ +Kendryte K210 Sysctl + +This binding describes the K210 sysctl device, which contains many miscellaneous +registers controlling system functionality. This node is a register map and can +be reference by other bindings which need a phandle to the K210 sysctl regmap. + +Required properties: +- compatible: should be + "canaan,k210-sysctl", "syscon", "simple-mfd" +- reg: address and length of the sysctl registers +- reg-io-width: must be <4> + +Clock sub-node + +This node is a binding for the clock tree driver + +Required properties: +- compatible: should be "canaan,k210-clk" +- clocks: phandle to the "in0" external oscillator +- #clock-cells: must be <1> + +Example: +sysctl: syscon@50440000 { + compatible = "canaan,k210-sysctl", "syscon", "simple-mfd"; + reg = <0x50440000 0x100>; + reg-io-width = <4>; + + sysclk: clock-controller { + compatible = "canaan,k210-clk"; + clocks = <&in0>; + #clock-cells = <1>; + }; +}; diff --git a/doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt b/doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt deleted file mode 100644 index 5b24abcb62..0000000000 --- a/doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt +++ /dev/null @@ -1,33 +0,0 @@ -Kendryte K210 Sysctl - -This binding describes the K210 sysctl device, which contains many miscellaneous -registers controlling system functionality. This node is a register map and can -be reference by other bindings which need a phandle to the K210 sysctl regmap. - -Required properties: -- compatible: should be - "kendryte,k210-sysctl", "syscon", "simple-mfd" -- reg: address and length of the sysctl registers -- reg-io-width: must be <4> - -Clock sub-node - -This node is a binding for the clock tree driver - -Required properties: -- compatible: should be "kendryte,k210-clk" -- clocks: phandle to the "in0" external oscillator -- #clock-cells: must be <1> - -Example: -sysctl: syscon@50440000 { - compatible = "kendryte,k210-sysctl", "syscon", "simple-mfd"; - reg = <0x50440000 0x100>; - reg-io-width = <4>; - - sysclk: clock-controller { - compatible = "kendryte,k210-clk"; - clocks = <&in0>; - #clock-cells = <1>; - }; -}; diff --git a/doc/device-tree-bindings/pinctrl/canaan,k210-fpioa.txt b/doc/device-tree-bindings/pinctrl/canaan,k210-fpioa.txt new file mode 100644 index 0000000000..deca0cfab7 --- /dev/null +++ b/doc/device-tree-bindings/pinctrl/canaan,k210-fpioa.txt @@ -0,0 +1,102 @@ +Kendryte K210 FPIOA + +This binding describes the Fully-Programmable Input/Output Array (FPIOA) found +in Kendryte K210 SoCs. Any of the 256 functions can be mapped to any of the 48 +pins. + +Required properties: +- compatible: should be "canaan,k210-fpioa" +- reg: address and length of the FPIOA registers +- canaan,sysctl: phandle to the "sysctl" register map node +- canaan,k210-power-offset: offset in the register map of the power bank control + register (in bytes) + +Configuration nodes + +Pin configuration nodes are documented in pinctrl-bindings.txt + +Required properties for pin-configuration nodes or sub-nodes are: +- groups: list of power groups to which the configuration applies. Valid groups + are: + A0, A1, A2, B3, B4, B5, C6, C7 + (either this or "pinmux" must be specified) +- pinmux: integer array representing pin multiplexing configuration. In addition + to the 256 standard functions, each pin can also output the direction + indicator (DO) of any function. This signal is high whenever the function + would normally drive the output. Helper macros to ease assembling the "pinmux" + arguments from the pin and function are provided by the FPIOA header file at: + + Integer values in the "pinmux" argument list are assembled as: + ((PIN << 16) | (DO << 8) | (FUNC)) + Valid values for PIN are numbers 0 through 47. + Valid values for DO are 0 or 1. + Valid values for FUNC are numbers 0 through 255. For a complete list of + acceptable functions, consult the FPIOA header file. + (either this or "groups" must be specified) + +Optional properties for "pinmux" nodes are: + bias-disable, bias-pull-down, bias-pull-up, drive-strength, + drive-strength-ua, input-enable, input-disable, input-schmitt-enable, + input-schmitt-disable, output-low, output-high, output-enable, + output-disable, slew-rate, output-polarity-invert, input-polarity-invert + +Optional properties for "groups" nodes are: + power-source + +Notes on specific properties include: +- bias-pull-up, -down, and -pin-default: The pull strength cannot be configured. +- drive-strength: There are 8 drive strength settings between 11 and 50 mA. +- input- and output-polarity-invert: Invert the polarity of either the input or + the output, respectively. +- power-source: Controls the output voltage of a bank of pins. Either + K210_PC_POWER_1V8 or K210_PC_POWER_3V3 may be specified. +- slew-rate: Specifying this property reduces the slew rate. + +Example: +fpioa: pinmux@502B0000 { + compatible = "canaan,k210-fpioa"; + reg = <0x502B0000 0x100>; + canaan,k210-sysctl = <&sysctl>; + canaan,k210-power-offset = ; + + /* JTAG running at 3.3V and driven at 11 mA */ + fpioa_jtag: jtag { + voltage { + group = "A0"; + power-source = ; + }; + + jtag { + pinmux = , + , + , + ; + drive-strength = <11>; + } + }; + + /* I2C configured for use with a TCA9800 level shifter */ + fpioa_i2c: i2c { + i2c { + pinmux = , + ; + }; + + direction { + pinmux = ; + output-polarity-invert; + }; + }; + + /* UART with an active-high TX status LED */ + fpioa_uart1: uart1 { + uart { + pinmux = , + ; + }; + + status { + pinmux = ; + }; + }; +}; diff --git a/doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt b/doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt deleted file mode 100644 index 73871f5930..0000000000 --- a/doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt +++ /dev/null @@ -1,102 +0,0 @@ -Kendryte K210 FPIOA - -This binding describes the Fully-Programmable Input/Output Array (FPIOA) found -in Kendryte K210 SoCs. Any of the 256 functions can be mapped to any of the 48 -pins. - -Required properties: -- compatible: should be "kendryte,k210-fpioa" -- reg: address and length of the FPIOA registers -- kendryte,sysctl: phandle to the "sysctl" register map node -- kendryte,power-offset: offset in the register map of the power bank control - register (in bytes) - -Configuration nodes - -Pin configuration nodes are documented in pinctrl-bindings.txt - -Required properties for pin-configuration nodes or sub-nodes are: -- groups: list of power groups to which the configuration applies. Valid groups - are: - A0, A1, A2, B3, B4, B5, C6, C7 - (either this or "pinmux" must be specified) -- pinmux: integer array representing pin multiplexing configuration. In addition - to the 256 standard functions, each pin can also output the direction - indicator (DO) of any function. This signal is high whenever the function - would normally drive the output. Helper macros to ease assembling the "pinmux" - arguments from the pin and function are provided by the FPIOA header file at: - - Integer values in the "pinmux" argument list are assembled as: - ((PIN << 16) | (DO << 8) | (FUNC)) - Valid values for PIN are numbers 0 through 47. - Valid values for DO are 0 or 1. - Valid values for FUNC are numbers 0 through 255. For a complete list of - acceptable functions, consult the FPIOA header file. - (either this or "groups" must be specified) - -Optional properties for "pinmux" nodes are: - bias-disable, bias-pull-down, bias-pull-up, drive-strength, - drive-strength-ua, input-enable, input-disable, input-schmitt-enable, - input-schmitt-disable, output-low, output-high, output-enable, - output-disable, slew-rate, output-polarity-invert, input-polarity-invert - -Optional properties for "groups" nodes are: - power-source - -Notes on specific properties include: -- bias-pull-up, -down, and -pin-default: The pull strength cannot be configured. -- drive-strength: There are 8 drive strength settings between 11 and 50 mA. -- input- and output-polarity-invert: Invert the polarity of either the input or - the output, respectively. -- power-source: Controls the output voltage of a bank of pins. Either - K210_PC_POWER_1V8 or K210_PC_POWER_3V3 may be specified. -- slew-rate: Specifying this property reduces the slew rate. - -Example: -fpioa: pinmux@502B0000 { - compatible = "kendryte,k210-fpioa"; - reg = <0x502B0000 0x100>; - kendryte,sysctl = <&sysctl>; - kendryte,power-offset = ; - - /* JTAG running at 3.3V and driven at 11 mA */ - fpioa_jtag: jtag { - voltage { - group = "A0"; - power-source = ; - }; - - jtag { - pinmux = , - , - , - ; - drive-strength = <11>; - } - }; - - /* I2C configured for use with a TCA9800 level shifter */ - fpioa_i2c: i2c { - i2c { - pinmux = , - ; - }; - - direction { - pinmux = ; - output-polarity-invert; - }; - }; - - /* UART with an active-high TX status LED */ - fpioa_uart1: uart1 { - uart { - pinmux = , - ; - }; - - status { - pinmux = ; - }; - }; -}; diff --git a/doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt b/doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt index 8d2888fbe3..7a0f11c53b 100644 --- a/doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt +++ b/doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt @@ -5,8 +5,8 @@ Required properties: - compatible : One of "altr,socfpga-spi", "altr,socfpga-arria10-spi", - "canaan,kendryte-k210-spi", - "canaan,kendryte-k210-ssi", + "canaan,k210-spi", + "canaan,k210-ssi", "intel,stratix10-spi", "intel,agilex-spi", "mscc,ocelot-spi", diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index f922a7c323..bb4eee5d99 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -29,7 +29,7 @@ obj-$(CONFIG_CLK_BOSTON) += clk_boston.o obj-$(CONFIG_CLK_CDCE9XX) += clk-cdce9xx.o obj-$(CONFIG_CLK_EXYNOS) += exynos/ obj-$(CONFIG_CLK_HSDK) += clk-hsdk-cgu.o -obj-$(CONFIG_CLK_K210) += clk_kendryte.o +obj-$(CONFIG_CLK_K210) += clk_k210.o obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o obj-$(CONFIG_CLK_MPFS) += microchip/ obj-$(CONFIG_CLK_MVEBU) += mvebu/ diff --git a/drivers/clk/clk_k210.c b/drivers/clk/clk_k210.c new file mode 100644 index 0000000000..1961efaa5e --- /dev/null +++ b/drivers/clk/clk_k210.c @@ -0,0 +1,1344 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019-20 Sean Anderson + */ +#define LOG_CATEGORY UCLASS_CLK + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/** + * struct k210_clk_priv - K210 clock driver private data + * @base: The base address of the sysctl device + * @in0: The "in0" external oscillator + */ +struct k210_clk_priv { + void __iomem *base; + struct clk in0; +}; + +/* + * All parameters for different sub-clocks are collected into parameter arrays. + * These parameters are then initialized by the clock which uses them during + * probe. To save space, ids are automatically generated for each sub-clock by + * using an enum. Instead of storing a parameter struct for each clock, even for + * those clocks which don't use a particular type of sub-clock, we can just + * store the parameters for the clocks which need them. + * + * So why do it like this? Arranging all the sub-clocks together makes it very + * easy to find bugs in the code. + */ + +/** + * enum k210_clk_div_type - The type of divider + * @K210_DIV_ONE: freq = parent / (reg + 1) + * @K210_DIV_EVEN: freq = parent / 2 / (reg + 1) + * @K210_DIV_POWER: freq = parent / (2 << reg) + * @K210_DIV_FIXED: freq = parent / factor + */ +enum k210_clk_div_type { + K210_DIV_ONE, + K210_DIV_EVEN, + K210_DIV_POWER, + K210_DIV_FIXED, +}; + +/** + * struct k210_div_params - Parameters for dividing clocks + * @type: An &enum k210_clk_div_type specifying the dividing formula + * @off: The offset of the divider from the sysctl base address + * @shift: The offset of the LSB of the divider + * @width: The number of bits in the divider + * @div: The fixed divisor for this divider + */ +struct k210_div_params { + u8 type; + union { + struct { + u8 off; + u8 shift; + u8 width; + }; + u8 div; + }; +}; + +#define DIV_LIST \ + DIV(K210_CLK_ACLK, K210_SYSCTL_SEL0, 1, 2, K210_DIV_POWER) \ + DIV(K210_CLK_APB0, K210_SYSCTL_SEL0, 3, 3, K210_DIV_ONE) \ + DIV(K210_CLK_APB1, K210_SYSCTL_SEL0, 6, 3, K210_DIV_ONE) \ + DIV(K210_CLK_APB2, K210_SYSCTL_SEL0, 9, 3, K210_DIV_ONE) \ + DIV(K210_CLK_SRAM0, K210_SYSCTL_THR0, 0, 4, K210_DIV_ONE) \ + DIV(K210_CLK_SRAM1, K210_SYSCTL_THR0, 4, 4, K210_DIV_ONE) \ + DIV(K210_CLK_AI, K210_SYSCTL_THR0, 8, 4, K210_DIV_ONE) \ + DIV(K210_CLK_DVP, K210_SYSCTL_THR0, 12, 4, K210_DIV_ONE) \ + DIV(K210_CLK_ROM, K210_SYSCTL_THR0, 16, 4, K210_DIV_ONE) \ + DIV(K210_CLK_SPI0, K210_SYSCTL_THR1, 0, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_SPI1, K210_SYSCTL_THR1, 8, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_SPI2, K210_SYSCTL_THR1, 16, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_SPI3, K210_SYSCTL_THR1, 24, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_TIMER0, K210_SYSCTL_THR2, 0, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_TIMER1, K210_SYSCTL_THR2, 8, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_TIMER2, K210_SYSCTL_THR2, 16, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_I2S0, K210_SYSCTL_THR3, 0, 16, K210_DIV_EVEN) \ + DIV(K210_CLK_I2S1, K210_SYSCTL_THR3, 16, 16, K210_DIV_EVEN) \ + DIV(K210_CLK_I2S2, K210_SYSCTL_THR4, 0, 16, K210_DIV_EVEN) \ + DIV(K210_CLK_I2S0_M, K210_SYSCTL_THR4, 16, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_I2S1_M, K210_SYSCTL_THR4, 24, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_I2S2_M, K210_SYSCTL_THR4, 0, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_I2C0, K210_SYSCTL_THR5, 8, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_I2C1, K210_SYSCTL_THR5, 16, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_I2C2, K210_SYSCTL_THR5, 24, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_WDT0, K210_SYSCTL_THR6, 0, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_WDT1, K210_SYSCTL_THR6, 8, 8, K210_DIV_EVEN) \ + DIV_FIXED(K210_CLK_CLINT, 50) \ + +#define _DIVIFY(id) K210_CLK_DIV_##id +#define DIVIFY(id) _DIVIFY(id) + +enum k210_div_id { +#define DIV(id, ...) DIVIFY(id), +#define DIV_FIXED DIV + DIV_LIST +#undef DIV +#undef DIV_FIXED + K210_CLK_DIV_NONE, +}; + +static const struct k210_div_params k210_divs[] = { +#define DIV(id, _off, _shift, _width, _type) \ + [DIVIFY(id)] = { \ + .type = (_type), \ + .off = (_off), \ + .shift = (_shift), \ + .width = (_width), \ + }, +#define DIV_FIXED(id, _div) \ + [DIVIFY(id)] = { \ + .type = K210_DIV_FIXED, \ + .div = (_div) \ + }, + DIV_LIST +#undef DIV +#undef DIV_FIXED +}; + +#undef DIV +#undef DIV_LIST + +/** + * struct k210_gate_params - Parameters for gated clocks + * @off: The offset of the gate from the sysctl base address + * @bit_idx: The index of the bit within the register + */ +struct k210_gate_params { + u8 off; + u8 bit_idx; +}; + +#define GATE_LIST \ + GATE(K210_CLK_CPU, K210_SYSCTL_EN_CENT, 0) \ + GATE(K210_CLK_SRAM0, K210_SYSCTL_EN_CENT, 1) \ + GATE(K210_CLK_SRAM1, K210_SYSCTL_EN_CENT, 2) \ + GATE(K210_CLK_APB0, K210_SYSCTL_EN_CENT, 3) \ + GATE(K210_CLK_APB1, K210_SYSCTL_EN_CENT, 4) \ + GATE(K210_CLK_APB2, K210_SYSCTL_EN_CENT, 5) \ + GATE(K210_CLK_ROM, K210_SYSCTL_EN_PERI, 0) \ + GATE(K210_CLK_DMA, K210_SYSCTL_EN_PERI, 1) \ + GATE(K210_CLK_AI, K210_SYSCTL_EN_PERI, 2) \ + GATE(K210_CLK_DVP, K210_SYSCTL_EN_PERI, 3) \ + GATE(K210_CLK_FFT, K210_SYSCTL_EN_PERI, 4) \ + GATE(K210_CLK_GPIO, K210_SYSCTL_EN_PERI, 5) \ + GATE(K210_CLK_SPI0, K210_SYSCTL_EN_PERI, 6) \ + GATE(K210_CLK_SPI1, K210_SYSCTL_EN_PERI, 7) \ + GATE(K210_CLK_SPI2, K210_SYSCTL_EN_PERI, 8) \ + GATE(K210_CLK_SPI3, K210_SYSCTL_EN_PERI, 9) \ + GATE(K210_CLK_I2S0, K210_SYSCTL_EN_PERI, 10) \ + GATE(K210_CLK_I2S1, K210_SYSCTL_EN_PERI, 11) \ + GATE(K210_CLK_I2S2, K210_SYSCTL_EN_PERI, 12) \ + GATE(K210_CLK_I2C0, K210_SYSCTL_EN_PERI, 13) \ + GATE(K210_CLK_I2C1, K210_SYSCTL_EN_PERI, 14) \ + GATE(K210_CLK_I2C2, K210_SYSCTL_EN_PERI, 15) \ + GATE(K210_CLK_UART1, K210_SYSCTL_EN_PERI, 16) \ + GATE(K210_CLK_UART2, K210_SYSCTL_EN_PERI, 17) \ + GATE(K210_CLK_UART3, K210_SYSCTL_EN_PERI, 18) \ + GATE(K210_CLK_AES, K210_SYSCTL_EN_PERI, 19) \ + GATE(K210_CLK_FPIOA, K210_SYSCTL_EN_PERI, 20) \ + GATE(K210_CLK_TIMER0, K210_SYSCTL_EN_PERI, 21) \ + GATE(K210_CLK_TIMER1, K210_SYSCTL_EN_PERI, 22) \ + GATE(K210_CLK_TIMER2, K210_SYSCTL_EN_PERI, 23) \ + GATE(K210_CLK_WDT0, K210_SYSCTL_EN_PERI, 24) \ + GATE(K210_CLK_WDT1, K210_SYSCTL_EN_PERI, 25) \ + GATE(K210_CLK_SHA, K210_SYSCTL_EN_PERI, 26) \ + GATE(K210_CLK_OTP, K210_SYSCTL_EN_PERI, 27) \ + GATE(K210_CLK_RTC, K210_SYSCTL_EN_PERI, 29) + +#define _GATEIFY(id) K210_CLK_GATE_##id +#define GATEIFY(id) _GATEIFY(id) + +enum k210_gate_id { +#define GATE(id, ...) GATEIFY(id), + GATE_LIST +#undef GATE + K210_CLK_GATE_NONE, +}; + +static const struct k210_gate_params k210_gates[] = { +#define GATE(id, _off, _idx) \ + [GATEIFY(id)] = { \ + .off = (_off), \ + .bit_idx = (_idx), \ + }, + GATE_LIST +#undef GATE +}; + +#undef GATE_LIST + +/* The most parents is PLL2 */ +#define K210_CLK_MAX_PARENTS 3 + +/** + * struct k210_mux_params - Parameters for muxed clocks + * @parents: A list of parent clock ids + * @num_parents: The number of parent clocks + * @off: The offset of the mux from the base sysctl address + * @shift: The offset of the LSB of the mux selector + * @width: The number of bits in the mux selector + */ +struct k210_mux_params { + u8 parents[K210_CLK_MAX_PARENTS]; + u8 num_parents; + u8 off; + u8 shift; + u8 width; +}; + +#define MUX(id, reg, shift, width) \ + MUX_PARENTS(id, reg, shift, width, K210_CLK_IN0, K210_CLK_PLL0) +#define MUX_LIST \ + MUX_PARENTS(K210_CLK_PLL2, K210_SYSCTL_PLL2, 26, 2, \ + K210_CLK_IN0, K210_CLK_PLL0, K210_CLK_PLL1) \ + MUX(K210_CLK_ACLK, K210_SYSCTL_SEL0, 0, 1) \ + MUX(K210_CLK_SPI3, K210_SYSCTL_SEL0, 12, 1) \ + MUX(K210_CLK_TIMER0, K210_SYSCTL_SEL0, 13, 1) \ + MUX(K210_CLK_TIMER1, K210_SYSCTL_SEL0, 14, 1) \ + MUX(K210_CLK_TIMER2, K210_SYSCTL_SEL0, 15, 1) + +#define _MUXIFY(id) K210_CLK_MUX_##id +#define MUXIFY(id) _MUXIFY(id) + +enum k210_mux_id { +#define MUX_PARENTS(id, ...) MUXIFY(id), + MUX_LIST +#undef MUX_PARENTS + K210_CLK_MUX_NONE, +}; + +static const struct k210_mux_params k210_muxes[] = { +#define MUX_PARENTS(id, _off, _shift, _width, ...) \ + [MUXIFY(id)] = { \ + .parents = { __VA_ARGS__ }, \ + .num_parents = __count_args(__VA_ARGS__), \ + .off = (_off), \ + .shift = (_shift), \ + .width = (_width), \ + }, + MUX_LIST +#undef MUX_PARENTS +}; + +#undef MUX +#undef MUX_LIST + +/** + * struct k210_pll_params - K210 PLL parameters + * @off: The offset of the PLL from the base sysctl address + * @shift: The offset of the LSB of the lock status + * @width: The number of bits in the lock status + */ +struct k210_pll_params { + u8 off; + u8 shift; + u8 width; +}; + +static const struct k210_pll_params k210_plls[] = { +#define PLL(_off, _shift, _width) { \ + .off = (_off), \ + .shift = (_shift), \ + .width = (_width), \ +} + [0] = PLL(K210_SYSCTL_PLL0, 0, 2), + [1] = PLL(K210_SYSCTL_PLL1, 8, 1), + [2] = PLL(K210_SYSCTL_PLL2, 16, 1), +#undef PLL +}; + +/** + * enum k210_clk_flags - The type of a K210 clock + * @K210_CLKF_MUX: This clock has a mux and not a static parent + * @K210_CLKF_PLL: This clock is a PLL + */ +enum k210_clk_flags { + K210_CLKF_MUX = BIT(0), + K210_CLKF_PLL = BIT(1), +}; + +/** + * struct k210_clk_params - The parameters defining a K210 clock + * @name: The name of the clock + * @flags: A set of &enum k210_clk_flags defining which fields are valid + * @mux: An &enum k210_mux_id of this clock's mux + * @parent: The clock id of this clock's parent + * @pll: The id of the PLL (if this clock is a PLL) + * @div: An &enum k210_div_id of this clock's divider + * @gate: An &enum k210_gate_id of this clock's gate + */ +struct k210_clk_params { +#if CONFIG_IS_ENABLED(CMD_CLK) + const char *name; +#endif + u8 flags; + union { + u8 parent; + u8 mux; + }; + union { + u8 pll; + struct { + u8 div; + u8 gate; + }; + }; +}; + +static const struct k210_clk_params k210_clks[] = { +#if CONFIG_IS_ENABLED(CMD_CLK) +#define NAME(_name) .name = (_name), +#else +#define NAME(name) +#endif +#define CLK(id, _name, _parent, _div, _gate) \ + [id] = { \ + NAME(_name) \ + .parent = (_parent), \ + .div = (_div), \ + .gate = (_gate), \ + } +#define CLK_MUX(id, _name, _mux, _div, _gate) \ + [id] = { \ + NAME(_name) \ + .flags = K210_CLKF_MUX, \ + .mux = (_mux), \ + .div = (_div), \ + .gate = (_gate), \ + } +#define CLK_PLL(id, _pll, _parent) \ + [id] = { \ + NAME("pll" #_pll) \ + .flags = K210_CLKF_PLL, \ + .parent = (_parent), \ + .pll = (_pll), \ + } +#define CLK_FULL(id, name) \ + CLK_MUX(id, name, MUXIFY(id), DIVIFY(id), GATEIFY(id)) +#define CLK_NOMUX(id, name, parent) \ + CLK(id, name, parent, DIVIFY(id), GATEIFY(id)) +#define CLK_DIV(id, name, parent) \ + CLK(id, name, parent, DIVIFY(id), K210_CLK_GATE_NONE) +#define CLK_GATE(id, name, parent) \ + CLK(id, name, parent, K210_CLK_DIV_NONE, GATEIFY(id)) + CLK_PLL(K210_CLK_PLL0, 0, K210_CLK_IN0), + CLK_PLL(K210_CLK_PLL1, 1, K210_CLK_IN0), + [K210_CLK_PLL2] = { + NAME("pll2") + .flags = K210_CLKF_MUX | K210_CLKF_PLL, + .mux = MUXIFY(K210_CLK_PLL2), + .pll = 2, + }, + CLK_MUX(K210_CLK_ACLK, "aclk", MUXIFY(K210_CLK_ACLK), + DIVIFY(K210_CLK_ACLK), K210_CLK_GATE_NONE), + CLK_FULL(K210_CLK_SPI3, "spi3"), + CLK_FULL(K210_CLK_TIMER0, "timer0"), + CLK_FULL(K210_CLK_TIMER1, "timer1"), + CLK_FULL(K210_CLK_TIMER2, "timer2"), + CLK_NOMUX(K210_CLK_SRAM0, "sram0", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_SRAM1, "sram1", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_ROM, "rom", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_DVP, "dvp", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_APB0, "apb0", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_APB1, "apb1", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_APB2, "apb2", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_AI, "ai", K210_CLK_PLL1), + CLK_NOMUX(K210_CLK_I2S0, "i2s0", K210_CLK_PLL2), + CLK_NOMUX(K210_CLK_I2S1, "i2s1", K210_CLK_PLL2), + CLK_NOMUX(K210_CLK_I2S2, "i2s2", K210_CLK_PLL2), + CLK_NOMUX(K210_CLK_WDT0, "wdt0", K210_CLK_IN0), + CLK_NOMUX(K210_CLK_WDT1, "wdt1", K210_CLK_IN0), + CLK_NOMUX(K210_CLK_SPI0, "spi0", K210_CLK_PLL0), + CLK_NOMUX(K210_CLK_SPI1, "spi1", K210_CLK_PLL0), + CLK_NOMUX(K210_CLK_SPI2, "spi2", K210_CLK_PLL0), + CLK_NOMUX(K210_CLK_I2C0, "i2c0", K210_CLK_PLL0), + CLK_NOMUX(K210_CLK_I2C1, "i2c1", K210_CLK_PLL0), + CLK_NOMUX(K210_CLK_I2C2, "i2c2", K210_CLK_PLL0), + CLK_DIV(K210_CLK_I2S0_M, "i2s0_m", K210_CLK_PLL2), + CLK_DIV(K210_CLK_I2S1_M, "i2s1_m", K210_CLK_PLL2), + CLK_DIV(K210_CLK_I2S2_M, "i2s2_m", K210_CLK_PLL2), + CLK_DIV(K210_CLK_CLINT, "clint", K210_CLK_ACLK), + CLK_GATE(K210_CLK_CPU, "cpu", K210_CLK_ACLK), + CLK_GATE(K210_CLK_DMA, "dma", K210_CLK_ACLK), + CLK_GATE(K210_CLK_FFT, "fft", K210_CLK_ACLK), + CLK_GATE(K210_CLK_GPIO, "gpio", K210_CLK_APB0), + CLK_GATE(K210_CLK_UART1, "uart1", K210_CLK_APB0), + CLK_GATE(K210_CLK_UART2, "uart2", K210_CLK_APB0), + CLK_GATE(K210_CLK_UART3, "uart3", K210_CLK_APB0), + CLK_GATE(K210_CLK_FPIOA, "fpioa", K210_CLK_APB0), + CLK_GATE(K210_CLK_SHA, "sha", K210_CLK_APB0), + CLK_GATE(K210_CLK_AES, "aes", K210_CLK_APB1), + CLK_GATE(K210_CLK_OTP, "otp", K210_CLK_APB1), + CLK_GATE(K210_CLK_RTC, "rtc", K210_CLK_IN0), +#undef NAME +#undef CLK_PLL +#undef CLK +#undef CLK_FULL +#undef CLK_NOMUX +#undef CLK_DIV +#undef CLK_GATE +#undef CLK_LIST +}; + +#define K210_PLL_CLKR GENMASK(3, 0) +#define K210_PLL_CLKF GENMASK(9, 4) +#define K210_PLL_CLKOD GENMASK(13, 10) /* Output Divider */ +#define K210_PLL_BWADJ GENMASK(19, 14) /* BandWidth Adjust */ +#define K210_PLL_RESET BIT(20) +#define K210_PLL_PWRD BIT(21) /* PoWeReD */ +#define K210_PLL_INTFB BIT(22) /* Internal FeedBack */ +#define K210_PLL_BYPASS BIT(23) +#define K210_PLL_TEST BIT(24) +#define K210_PLL_EN BIT(25) +#define K210_PLL_TEST_EN BIT(26) + +#define K210_PLL_LOCK 0 +#define K210_PLL_CLEAR_SLIP 2 +#define K210_PLL_TEST_OUT 3 + +#ifdef CONFIG_CLK_K210_SET_RATE +static int k210_pll_enable(struct k210_clk_priv *priv, int id); +static int k210_pll_disable(struct k210_clk_priv *priv, int id); +static ulong k210_pll_get_rate(struct k210_clk_priv *priv, int id, ulong rate_in); + +/* + * The PLL included with the Kendryte K210 appears to be a True Circuits, Inc. + * General-Purpose PLL. The logical layout of the PLL with internal feedback is + * approximately the following: + * + * +---------------+ + * |reference clock| + * +---------------+ + * | + * v + * +--+ + * |/r| + * +--+ + * | + * v + * +-------------+ + * |divided clock| + * +-------------+ + * | + * v + * +--------------+ + * |phase detector|<---+ + * +--------------+ | + * | | + * v +--------------+ + * +---+ |feedback clock| + * |VCO| +--------------+ + * +---+ ^ + * | +--+ | + * +--->|/f|---+ + * | +--+ + * v + * +---+ + * |/od| + * +---+ + * | + * v + * +------+ + * |output| + * +------+ + * + * The k210 PLLs have three factors: r, f, and od. Because of the feedback mode, + * the effect of the division by f is to multiply the input frequency. The + * equation for the output rate is + * rate = (rate_in * f) / (r * od). + * Moving knowns to one side of the equation, we get + * rate / rate_in = f / (r * od) + * Rearranging slightly, + * abs_error = abs((rate / rate_in) - (f / (r * od))). + * To get relative, error, we divide by the expected ratio + * error = abs((rate / rate_in) - (f / (r * od))) / (rate / rate_in). + * Simplifying, + * error = abs(1 - f / (r * od)) / (rate / rate_in) + * error = abs(1 - (f * rate_in) / (r * od * rate)) + * Using the constants ratio = rate / rate_in and inv_ratio = rate_in / rate, + * error = abs((f * inv_ratio) / (r * od) - 1) + * This is the error used in evaluating parameters. + * + * r and od are four bits each, while f is six bits. Because r and od are + * multiplied together, instead of the full 256 values possible if both bits + * were used fully, there are only 97 distinct products. Combined with f, there + * are 6208 theoretical settings for the PLL. However, most of these settings + * can be ruled out immediately because they do not have the correct ratio. + * + * In addition to the constraint of approximating the desired ratio, parameters + * must also keep internal pll frequencies within acceptable ranges. The divided + * clock's minimum and maximum frequencies have a ratio of around 128. This + * leaves fairly substantial room to work with, especially since the only + * affected parameter is r. The VCO's minimum and maximum frequency have a ratio + * of 5, which is considerably more restrictive. + * + * The r and od factors are stored in a table. This is to make it easy to find + * the next-largest product. Some products have multiple factorizations, but + * only when one factor has at least a 2.5x ratio to the factors of the other + * factorization. This is because any smaller ratio would not make a difference + * when ensuring the VCO's frequency is within spec. + * + * Throughout the calculation function, fixed point arithmetic is used. Because + * the range of rate and rate_in may be up to 1.75 GHz, or around 2^30, 64-bit + * 32.32 fixed-point numbers are used to represent ratios. In general, to + * implement division, the numerator is first multiplied by 2^32. This gives a + * result where the whole number part is in the upper 32 bits, and the fraction + * is in the lower 32 bits. + * + * In general, rounding is done to the closest integer. This helps find the best + * approximation for the ratio. Rounding in one direction (e.g down) could cause + * the function to miss a better ratio with one of the parameters increased by + * one. + */ + +/* + * The factors table was generated with the following python code: + * + * def p(x, y): + * return (1.0*x/y > 2.5) or (1.0*y/x > 2.5) + * + * factors = {} + * for i in range(1, 17): + * for j in range(1, 17): + * fs = factors.get(i*j) or [] + * if fs == [] or all([ + * (p(i, x) and p(i, y)) or (p(j, x) and p(j, y)) + * for (x, y) in fs]): + * fs.append((i, j)) + * factors[i*j] = fs + * + * for k, l in sorted(factors.items()): + * for v in l: + * print("PACK(%s, %s)," % v) + */ +#define PACK(r, od) (((((r) - 1) & 0xF) << 4) | (((od) - 1) & 0xF)) +#define UNPACK_R(val) ((((val) >> 4) & 0xF) + 1) +#define UNPACK_OD(val) (((val) & 0xF) + 1) +static const u8 factors[] = { + PACK(1, 1), + PACK(1, 2), + PACK(1, 3), + PACK(1, 4), + PACK(1, 5), + PACK(1, 6), + PACK(1, 7), + PACK(1, 8), + PACK(1, 9), + PACK(3, 3), + PACK(1, 10), + PACK(1, 11), + PACK(1, 12), + PACK(3, 4), + PACK(1, 13), + PACK(1, 14), + PACK(1, 15), + PACK(3, 5), + PACK(1, 16), + PACK(4, 4), + PACK(2, 9), + PACK(2, 10), + PACK(3, 7), + PACK(2, 11), + PACK(2, 12), + PACK(5, 5), + PACK(2, 13), + PACK(3, 9), + PACK(2, 14), + PACK(2, 15), + PACK(2, 16), + PACK(3, 11), + PACK(5, 7), + PACK(3, 12), + PACK(3, 13), + PACK(4, 10), + PACK(3, 14), + PACK(4, 11), + PACK(3, 15), + PACK(3, 16), + PACK(7, 7), + PACK(5, 10), + PACK(4, 13), + PACK(6, 9), + PACK(5, 11), + PACK(4, 14), + PACK(4, 15), + PACK(7, 9), + PACK(4, 16), + PACK(5, 13), + PACK(6, 11), + PACK(5, 14), + PACK(6, 12), + PACK(5, 15), + PACK(7, 11), + PACK(6, 13), + PACK(5, 16), + PACK(9, 9), + PACK(6, 14), + PACK(8, 11), + PACK(6, 15), + PACK(7, 13), + PACK(6, 16), + PACK(7, 14), + PACK(9, 11), + PACK(10, 10), + PACK(8, 13), + PACK(7, 15), + PACK(9, 12), + PACK(10, 11), + PACK(7, 16), + PACK(9, 13), + PACK(8, 15), + PACK(11, 11), + PACK(9, 14), + PACK(8, 16), + PACK(10, 13), + PACK(11, 12), + PACK(9, 15), + PACK(10, 14), + PACK(11, 13), + PACK(9, 16), + PACK(10, 15), + PACK(11, 14), + PACK(12, 13), + PACK(10, 16), + PACK(11, 15), + PACK(12, 14), + PACK(13, 13), + PACK(11, 16), + PACK(12, 15), + PACK(13, 14), + PACK(12, 16), + PACK(13, 15), + PACK(14, 14), + PACK(13, 16), + PACK(14, 15), + PACK(14, 16), + PACK(15, 15), + PACK(15, 16), + PACK(16, 16), +}; + +TEST_STATIC int k210_pll_calc_config(u32 rate, u32 rate_in, + struct k210_pll_config *best) +{ + int i; + s64 error, best_error; + u64 ratio, inv_ratio; /* fixed point 32.32 ratio of the rates */ + u64 max_r; + u64 r, f, od; + + /* + * Can't go over 1.75 GHz or under 21.25 MHz due to limitations on the + * VCO frequency. These are not the same limits as below because od can + * reduce the output frequency by 16. + */ + if (rate > 1750000000 || rate < 21250000) + return -EINVAL; + + /* Similar restrictions on the input rate */ + if (rate_in > 1750000000 || rate_in < 13300000) + return -EINVAL; + + ratio = DIV_ROUND_CLOSEST_ULL((u64)rate << 32, rate_in); + inv_ratio = DIV_ROUND_CLOSEST_ULL((u64)rate_in << 32, rate); + /* Can't increase by more than 64 or reduce by more than 256 */ + if (rate > rate_in && ratio > (64ULL << 32)) + return -EINVAL; + else if (rate <= rate_in && inv_ratio > (256ULL << 32)) + return -EINVAL; + + /* + * The divided clock (rate_in / r) must stay between 1.75 GHz and 13.3 + * MHz. There is no minimum, since the only way to get a higher input + * clock than 26 MHz is to use a clock generated by a PLL. Because PLLs + * cannot output frequencies greater than 1.75 GHz, the minimum would + * never be greater than one. + */ + max_r = DIV_ROUND_DOWN_ULL(rate_in, 13300000); + + /* Variables get immediately incremented, so start at -1th iteration */ + i = -1; + f = 0; + r = 0; + od = 0; + best_error = S64_MAX; + error = best_error; + /* do-while here so we always try at least one ratio */ + do { + /* + * Whether we swapped r and od while enforcing frequency limits + */ + bool swapped = false; + /* + * Whether the intermediate frequencies are out-of-spec + */ + bool out_of_spec; + u64 last_od = od; + u64 last_r = r; + + /* + * Try the next largest value for f (or r and od) and + * recalculate the other parameters based on that + */ + if (rate > rate_in) { + /* + * Skip factors of the same product if we already tried + * out that product + */ + do { + i++; + r = UNPACK_R(factors[i]); + od = UNPACK_OD(factors[i]); + } while (i + 1 < ARRAY_SIZE(factors) && + r * od == last_r * last_od); + + /* Round close */ + f = (r * od * ratio + BIT(31)) >> 32; + if (f > 64) + f = 64; + } else { + u64 tmp = ++f * inv_ratio; + bool round_up = !!(tmp & BIT(31)); + u32 goal = (tmp >> 32) + round_up; + u32 err, last_err; + + /* Get the next r/od pair in factors */ + while (r * od < goal && i + 1 < ARRAY_SIZE(factors)) { + i++; + r = UNPACK_R(factors[i]); + od = UNPACK_OD(factors[i]); + } + + /* + * This is a case of double rounding. If we rounded up + * above, we need to round down (in cases of ties) here. + * This prevents off-by-one errors resulting from + * choosing X+2 over X when X.Y rounds up to X+1 and + * there is no r * od = X+1. For the converse, when X.Y + * is rounded down to X, we should choose X+1 over X-1. + */ + err = abs(r * od - goal); + last_err = abs(last_r * last_od - goal); + if (last_err < err || (round_up && last_err == err)) { + i--; + r = last_r; + od = last_od; + } + } + + /* + * Enforce limits on internal clock frequencies. If we + * aren't in spec, try swapping r and od. If everything is + * in-spec, calculate the relative error. + */ +again: + out_of_spec = false; + if (r > max_r) { + out_of_spec = true; + } else { + /* + * There is no way to only divide once; we need + * to examine the frequency with and without the + * effect of od. + */ + u64 vco = DIV_ROUND_CLOSEST_ULL(rate_in * f, r); + + if (vco > 1750000000 || vco < 340000000) + out_of_spec = true; + } + + if (out_of_spec) { + u64 new_r, new_od; + + if (!swapped) { + u64 tmp = r; + + r = od; + od = tmp; + swapped = true; + goto again; + } + + /* + * Try looking ahead to see if there are additional + * factors for the same product. + */ + if (i + 1 < ARRAY_SIZE(factors)) { + i++; + new_r = UNPACK_R(factors[i]); + new_od = UNPACK_OD(factors[i]); + if (r * od == new_r * new_od) { + r = new_r; + od = new_od; + swapped = false; + goto again; + } + i--; + } + + /* + * Try looking back to see if there is a worse ratio + * that we could try anyway + */ + while (i > 0) { + i--; + new_r = UNPACK_R(factors[i]); + new_od = UNPACK_OD(factors[i]); + /* + * Don't loop over factors for the same product + * to avoid getting stuck because of the above + * clause + */ + if (r * od != new_r * new_od) { + if (new_r * new_od > last_r * last_od) { + r = new_r; + od = new_od; + swapped = false; + goto again; + } + break; + } + } + + /* We ran out of things to try */ + continue; + } + + error = DIV_ROUND_CLOSEST_ULL(f * inv_ratio, r * od); + /* The lower 16 bits are spurious */ + error = abs((error - BIT(32))) >> 16; + + if (error < best_error) { + best->r = r; + best->f = f; + best->od = od; + best_error = error; + } + } while (f < 64 && i + 1 < ARRAY_SIZE(factors) && error != 0); + + log_debug("best error %lld\n", best_error); + if (best_error == S64_MAX) + return -EINVAL; + + return 0; +} + +static ulong k210_pll_set_rate(struct k210_clk_priv *priv, int id, ulong rate, + ulong rate_in) +{ + int err; + const struct k210_pll_params *pll = &k210_plls[id]; + struct k210_pll_config config = {}; + u32 reg; + ulong calc_rate; + + err = k210_pll_calc_config(rate, rate_in, &config); + if (err) + return err; + log_debug("Got r=%u f=%u od=%u\n", config.r, config.f, config.od); + + /* Don't bother setting the rate if we're already at that rate */ + calc_rate = DIV_ROUND_DOWN_ULL(((u64)rate_in) * config.f, + config.r * config.od); + if (calc_rate == k210_pll_get_rate(priv, id, rate)) + return calc_rate; + + k210_pll_disable(priv, id); + + reg = readl(priv->base + pll->off); + reg &= ~K210_PLL_CLKR + & ~K210_PLL_CLKF + & ~K210_PLL_CLKOD + & ~K210_PLL_BWADJ; + reg |= FIELD_PREP(K210_PLL_CLKR, config.r - 1) + | FIELD_PREP(K210_PLL_CLKF, config.f - 1) + | FIELD_PREP(K210_PLL_CLKOD, config.od - 1) + | FIELD_PREP(K210_PLL_BWADJ, config.f - 1); + writel(reg, priv->base + pll->off); + + k210_pll_enable(priv, id); + + serial_setbrg(); + return k210_pll_get_rate(priv, id, rate); +} +#else +static ulong k210_pll_set_rate(struct k210_clk_priv *priv, int id, ulong rate, + ulong rate_in) +{ + return -ENOSYS; +} +#endif /* CONFIG_CLK_K210_SET_RATE */ + +static ulong k210_pll_get_rate(struct k210_clk_priv *priv, int id, + ulong rate_in) +{ + u64 r, f, od; + u32 reg = readl(priv->base + k210_plls[id].off); + + if (reg & K210_PLL_BYPASS) + return rate_in; + + if (!(reg & K210_PLL_PWRD)) + return 0; + + r = FIELD_GET(K210_PLL_CLKR, reg) + 1; + f = FIELD_GET(K210_PLL_CLKF, reg) + 1; + od = FIELD_GET(K210_PLL_CLKOD, reg) + 1; + + return DIV_ROUND_DOWN_ULL(((u64)rate_in) * f, r * od); +} + +/* + * Wait for the PLL to be locked. If the PLL is not locked, try clearing the + * slip before retrying + */ +static void k210_pll_waitfor_lock(struct k210_clk_priv *priv, int id) +{ + const struct k210_pll_params *pll = &k210_plls[id]; + u32 mask = (BIT(pll->width) - 1) << pll->shift; + + while (true) { + u32 reg = readl(priv->base + K210_SYSCTL_PLL_LOCK); + + if ((reg & mask) == mask) + break; + + reg |= BIT(pll->shift + K210_PLL_CLEAR_SLIP); + writel(reg, priv->base + K210_SYSCTL_PLL_LOCK); + } +} + +static bool k210_pll_enabled(u32 reg) +{ + return (reg & K210_PLL_PWRD) && (reg & K210_PLL_EN) && + !(reg & K210_PLL_RESET); +} + +/* Adapted from sysctl_pll_enable */ +static int k210_pll_enable(struct k210_clk_priv *priv, int id) +{ + const struct k210_pll_params *pll = &k210_plls[id]; + u32 reg = readl(priv->base + pll->off); + + if (k210_pll_enabled(reg)) + return 0; + + reg |= K210_PLL_PWRD; + writel(reg, priv->base + pll->off); + + /* Ensure reset is low before asserting it */ + reg &= ~K210_PLL_RESET; + writel(reg, priv->base + pll->off); + reg |= K210_PLL_RESET; + writel(reg, priv->base + pll->off); + nop(); + nop(); + reg &= ~K210_PLL_RESET; + writel(reg, priv->base + pll->off); + + k210_pll_waitfor_lock(priv, id); + + reg &= ~K210_PLL_BYPASS; + reg |= K210_PLL_EN; + writel(reg, priv->base + pll->off); + + return 0; +} + +static int k210_pll_disable(struct k210_clk_priv *priv, int id) +{ + const struct k210_pll_params *pll = &k210_plls[id]; + u32 reg = readl(priv->base + pll->off); + + /* + * Bypassing before powering off is important so child clocks don't stop + * working. This is especially important for pll0, the indirect parent + * of the cpu clock. + */ + reg |= K210_PLL_BYPASS; + writel(reg, priv->base + pll->off); + + reg &= ~K210_PLL_PWRD; + reg &= ~K210_PLL_EN; + writel(reg, priv->base + pll->off); + return 0; +} + +static u32 k210_clk_readl(struct k210_clk_priv *priv, u8 off, u8 shift, + u8 width) +{ + u32 reg = readl(priv->base + off); + + return (reg >> shift) & (BIT(width) - 1); +} + +static void k210_clk_writel(struct k210_clk_priv *priv, u8 off, u8 shift, + u8 width, u32 val) +{ + u32 reg = readl(priv->base + off); + u32 mask = (BIT(width) - 1) << shift; + + reg &= ~mask; + reg |= mask & (val << shift); + writel(reg, priv->base + off); +} + +static int k210_clk_get_parent(struct k210_clk_priv *priv, int id) +{ + u32 sel; + const struct k210_mux_params *mux; + + if (!(k210_clks[id].flags & K210_CLKF_MUX)) + return k210_clks[id].parent; + mux = &k210_muxes[k210_clks[id].mux]; + + sel = k210_clk_readl(priv, mux->off, mux->shift, mux->width); + assert(sel < mux->num_parents); + return mux->parents[sel]; +} + +static ulong do_k210_clk_get_rate(struct k210_clk_priv *priv, int id) +{ + int parent; + u32 val; + ulong parent_rate; + const struct k210_div_params *div; + + if (id == K210_CLK_IN0) + return clk_get_rate(&priv->in0); + + parent = k210_clk_get_parent(priv, id); + parent_rate = do_k210_clk_get_rate(priv, parent); + if (IS_ERR_VALUE(parent_rate)) + return parent_rate; + + if (k210_clks[id].flags & K210_CLKF_PLL) + return k210_pll_get_rate(priv, k210_clks[id].pll, parent_rate); + + if (k210_clks[id].div == K210_CLK_DIV_NONE) + return parent_rate; + div = &k210_divs[k210_clks[id].div]; + + if (div->type == K210_DIV_FIXED) + return parent_rate / div->div; + + val = k210_clk_readl(priv, div->off, div->shift, div->width); + switch (div->type) { + case K210_DIV_ONE: + return parent_rate / (val + 1); + case K210_DIV_EVEN: + return parent_rate / 2 / (val + 1); + case K210_DIV_POWER: + /* This is ACLK, which has no divider on IN0 */ + if (parent == K210_CLK_IN0) + return parent_rate; + return parent_rate / (2 << val); + default: + assert(false); + return -EINVAL; + }; +} + +static ulong k210_clk_get_rate(struct clk *clk) +{ + return do_k210_clk_get_rate(dev_get_priv(clk->dev), clk->id); +} + +static int do_k210_clk_set_parent(struct k210_clk_priv *priv, int id, int new) +{ + int i; + const struct k210_mux_params *mux; + + if (!(k210_clks[id].flags & K210_CLKF_MUX)) + return -ENOSYS; + mux = &k210_muxes[k210_clks[id].mux]; + + for (i = 0; i < mux->num_parents; i++) { + if (mux->parents[i] == new) { + k210_clk_writel(priv, mux->off, mux->shift, mux->width, + i); + return 0; + } + } + return -EINVAL; +} + +static int k210_clk_set_parent(struct clk *clk, struct clk *parent) +{ + return do_k210_clk_set_parent(dev_get_priv(clk->dev), clk->id, + parent->id); +} + +static ulong k210_clk_set_rate(struct clk *clk, unsigned long rate) +{ + int parent, ret, err; + ulong rate_in, val; + const struct k210_div_params *div; + struct k210_clk_priv *priv = dev_get_priv(clk->dev); + + if (clk->id == K210_CLK_IN0) + return clk_set_rate(&priv->in0, rate); + + parent = k210_clk_get_parent(priv, clk->id); + rate_in = do_k210_clk_get_rate(priv, parent); + if (IS_ERR_VALUE(rate_in)) + return rate_in; + + log_debug("id=%ld rate=%lu rate_in=%lu\n", clk->id, rate, rate_in); + + if (clk->id == K210_CLK_PLL0) { + /* Bypass ACLK so the CPU keeps going */ + ret = do_k210_clk_set_parent(priv, K210_CLK_ACLK, K210_CLK_IN0); + if (ret) + return ret; + } else if (clk->id == K210_CLK_PLL1 && gd->flags & GD_FLG_RELOC) { + /* + * We can't bypass the AI clock like we can ACLK, and after + * relocation we are using the AI ram. + */ + return -EPERM; + } + + if (k210_clks[clk->id].flags & K210_CLKF_PLL) { + ret = k210_pll_set_rate(priv, k210_clks[clk->id].pll, rate, + rate_in); + if (!IS_ERR_VALUE(ret) && clk->id == K210_CLK_PLL0) { + /* + * This may have the side effect of reparenting ACLK, + * but I don't really want to keep track of what the old + * parent was. + */ + err = do_k210_clk_set_parent(priv, K210_CLK_ACLK, + K210_CLK_PLL0); + if (err) + return err; + } + return ret; + } + + if (k210_clks[clk->id].div == K210_CLK_DIV_NONE) + return -ENOSYS; + div = &k210_divs[k210_clks[clk->id].div]; + + switch (div->type) { + case K210_DIV_ONE: + val = DIV_ROUND_CLOSEST_ULL((u64)rate_in, rate); + val = val ? val - 1 : 0; + break; + case K210_DIV_EVEN: + val = DIV_ROUND_CLOSEST_ULL((u64)rate_in, 2 * rate); + break; + case K210_DIV_POWER: + /* This is ACLK, which has no divider on IN0 */ + if (parent == K210_CLK_IN0) + return -ENOSYS; + + val = DIV_ROUND_CLOSEST_ULL((u64)rate_in, rate); + val = __ffs(val); + break; + default: + assert(false); + return -EINVAL; + }; + + val = val ? val - 1 : 0; + k210_clk_writel(priv, div->off, div->shift, div->width, val); + return do_k210_clk_get_rate(priv, clk->id); +} + +static int k210_clk_endisable(struct k210_clk_priv *priv, int id, bool enable) +{ + int parent = k210_clk_get_parent(priv, id); + const struct k210_gate_params *gate; + + if (id == K210_CLK_IN0) { + if (enable) + return clk_enable(&priv->in0); + else + return clk_disable(&priv->in0); + } + + /* Only recursively enable clocks since we don't track refcounts */ + if (enable) { + int ret = k210_clk_endisable(priv, parent, true); + + if (ret && ret != -ENOSYS) + return ret; + } + + if (k210_clks[id].flags & K210_CLKF_PLL) { + if (enable) + return k210_pll_enable(priv, k210_clks[id].pll); + else + return k210_pll_disable(priv, k210_clks[id].pll); + } + + if (k210_clks[id].gate == K210_CLK_GATE_NONE) + return -ENOSYS; + gate = &k210_gates[k210_clks[id].gate]; + + k210_clk_writel(priv, gate->off, gate->bit_idx, 1, enable); + return 0; +} + +static int k210_clk_enable(struct clk *clk) +{ + return k210_clk_endisable(dev_get_priv(clk->dev), clk->id, true); +} + +static int k210_clk_disable(struct clk *clk) +{ + return k210_clk_endisable(dev_get_priv(clk->dev), clk->id, false); +} + +static int k210_clk_request(struct clk *clk) +{ + if (clk->id >= ARRAY_SIZE(k210_clks)) + return -EINVAL; + return 0; +} + +static const struct clk_ops k210_clk_ops = { + .request = k210_clk_request, + .set_rate = k210_clk_set_rate, + .get_rate = k210_clk_get_rate, + .set_parent = k210_clk_set_parent, + .enable = k210_clk_enable, + .disable = k210_clk_disable, +}; + +static int k210_clk_probe(struct udevice *dev) +{ + int ret; + struct k210_clk_priv *priv = dev_get_priv(dev); + + priv->base = dev_read_addr_ptr(dev_get_parent(dev)); + if (!priv->base) + return -EINVAL; + + ret = clk_get_by_index(dev, 0, &priv->in0); + if (ret) + return ret; + + /* + * Force setting defaults, even before relocation. This is so we can + * set the clock rate for PLL1 before we relocate into aisram. + */ + if (!(gd->flags & GD_FLG_RELOC)) + clk_set_defaults(dev, CLK_DEFAULTS_POST_FORCE); + + return 0; +} + +static const struct udevice_id k210_clk_ids[] = { + { .compatible = "canaan,k210-clk" }, + { }, +}; + +U_BOOT_DRIVER(k210_clk) = { + .name = "k210_clk", + .id = UCLASS_CLK, + .of_match = k210_clk_ids, + .ops = &k210_clk_ops, + .probe = k210_clk_probe, + .priv_auto = sizeof(struct k210_clk_priv), +}; + +#if CONFIG_IS_ENABLED(CMD_CLK) +static char show_enabled(struct k210_clk_priv *priv, int id) +{ + bool enabled; + + if (k210_clks[id].flags & K210_CLKF_PLL) { + const struct k210_pll_params *pll = + &k210_plls[k210_clks[id].pll]; + + enabled = k210_pll_enabled(readl(priv->base + pll->off)); + } else if (k210_clks[id].gate == K210_CLK_GATE_NONE) { + return '-'; + } else { + const struct k210_gate_params *gate = + &k210_gates[k210_clks[id].gate]; + + enabled = k210_clk_readl(priv, gate->off, gate->bit_idx, 1); + } + + return enabled ? 'y' : 'n'; +} + +static void show_clks(struct k210_clk_priv *priv, int id, int depth) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(k210_clks); i++) { + if (k210_clk_get_parent(priv, i) != id) + continue; + + printf(" %-9lu %-7c %*s%s\n", do_k210_clk_get_rate(priv, i), + show_enabled(priv, i), depth * 4, "", + k210_clks[i].name); + + show_clks(priv, i, depth + 1); + } +} + +int soc_clk_dump(void) +{ + int ret; + struct udevice *dev; + struct k210_clk_priv *priv; + + ret = uclass_get_device_by_driver(UCLASS_CLK, DM_DRIVER_GET(k210_clk), + &dev); + if (ret) + return ret; + priv = dev_get_priv(dev); + + puts(" Rate Enabled Name\n"); + puts("------------------------\n"); + printf(" %-9lu %-7c %*s%s\n", clk_get_rate(&priv->in0), 'y', 0, "", + priv->in0.dev->name); + show_clks(priv, K210_CLK_IN0, 1); + return 0; +} +#endif diff --git a/drivers/clk/clk_kendryte.c b/drivers/clk/clk_kendryte.c deleted file mode 100644 index 97efda5b6f..0000000000 --- a/drivers/clk/clk_kendryte.c +++ /dev/null @@ -1,1344 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2019-20 Sean Anderson - */ -#define LOG_CATEGORY UCLASS_CLK - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -DECLARE_GLOBAL_DATA_PTR; - -/** - * struct k210_clk_priv - K210 clock driver private data - * @base: The base address of the sysctl device - * @in0: The "in0" external oscillator - */ -struct k210_clk_priv { - void __iomem *base; - struct clk in0; -}; - -/* - * All parameters for different sub-clocks are collected into parameter arrays. - * These parameters are then initialized by the clock which uses them during - * probe. To save space, ids are automatically generated for each sub-clock by - * using an enum. Instead of storing a parameter struct for each clock, even for - * those clocks which don't use a particular type of sub-clock, we can just - * store the parameters for the clocks which need them. - * - * So why do it like this? Arranging all the sub-clocks together makes it very - * easy to find bugs in the code. - */ - -/** - * enum k210_clk_div_type - The type of divider - * @K210_DIV_ONE: freq = parent / (reg + 1) - * @K210_DIV_EVEN: freq = parent / 2 / (reg + 1) - * @K210_DIV_POWER: freq = parent / (2 << reg) - * @K210_DIV_FIXED: freq = parent / factor - */ -enum k210_clk_div_type { - K210_DIV_ONE, - K210_DIV_EVEN, - K210_DIV_POWER, - K210_DIV_FIXED, -}; - -/** - * struct k210_div_params - Parameters for dividing clocks - * @type: An &enum k210_clk_div_type specifying the dividing formula - * @off: The offset of the divider from the sysctl base address - * @shift: The offset of the LSB of the divider - * @width: The number of bits in the divider - * @div: The fixed divisor for this divider - */ -struct k210_div_params { - u8 type; - union { - struct { - u8 off; - u8 shift; - u8 width; - }; - u8 div; - }; -}; - -#define DIV_LIST \ - DIV(K210_CLK_ACLK, K210_SYSCTL_SEL0, 1, 2, K210_DIV_POWER) \ - DIV(K210_CLK_APB0, K210_SYSCTL_SEL0, 3, 3, K210_DIV_ONE) \ - DIV(K210_CLK_APB1, K210_SYSCTL_SEL0, 6, 3, K210_DIV_ONE) \ - DIV(K210_CLK_APB2, K210_SYSCTL_SEL0, 9, 3, K210_DIV_ONE) \ - DIV(K210_CLK_SRAM0, K210_SYSCTL_THR0, 0, 4, K210_DIV_ONE) \ - DIV(K210_CLK_SRAM1, K210_SYSCTL_THR0, 4, 4, K210_DIV_ONE) \ - DIV(K210_CLK_AI, K210_SYSCTL_THR0, 8, 4, K210_DIV_ONE) \ - DIV(K210_CLK_DVP, K210_SYSCTL_THR0, 12, 4, K210_DIV_ONE) \ - DIV(K210_CLK_ROM, K210_SYSCTL_THR0, 16, 4, K210_DIV_ONE) \ - DIV(K210_CLK_SPI0, K210_SYSCTL_THR1, 0, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_SPI1, K210_SYSCTL_THR1, 8, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_SPI2, K210_SYSCTL_THR1, 16, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_SPI3, K210_SYSCTL_THR1, 24, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_TIMER0, K210_SYSCTL_THR2, 0, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_TIMER1, K210_SYSCTL_THR2, 8, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_TIMER2, K210_SYSCTL_THR2, 16, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_I2S0, K210_SYSCTL_THR3, 0, 16, K210_DIV_EVEN) \ - DIV(K210_CLK_I2S1, K210_SYSCTL_THR3, 16, 16, K210_DIV_EVEN) \ - DIV(K210_CLK_I2S2, K210_SYSCTL_THR4, 0, 16, K210_DIV_EVEN) \ - DIV(K210_CLK_I2S0_M, K210_SYSCTL_THR4, 16, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_I2S1_M, K210_SYSCTL_THR4, 24, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_I2S2_M, K210_SYSCTL_THR4, 0, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_I2C0, K210_SYSCTL_THR5, 8, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_I2C1, K210_SYSCTL_THR5, 16, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_I2C2, K210_SYSCTL_THR5, 24, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_WDT0, K210_SYSCTL_THR6, 0, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_WDT1, K210_SYSCTL_THR6, 8, 8, K210_DIV_EVEN) \ - DIV_FIXED(K210_CLK_CLINT, 50) \ - -#define _DIVIFY(id) K210_CLK_DIV_##id -#define DIVIFY(id) _DIVIFY(id) - -enum k210_div_id { -#define DIV(id, ...) DIVIFY(id), -#define DIV_FIXED DIV - DIV_LIST -#undef DIV -#undef DIV_FIXED - K210_CLK_DIV_NONE, -}; - -static const struct k210_div_params k210_divs[] = { -#define DIV(id, _off, _shift, _width, _type) \ - [DIVIFY(id)] = { \ - .type = (_type), \ - .off = (_off), \ - .shift = (_shift), \ - .width = (_width), \ - }, -#define DIV_FIXED(id, _div) \ - [DIVIFY(id)] = { \ - .type = K210_DIV_FIXED, \ - .div = (_div) \ - }, - DIV_LIST -#undef DIV -#undef DIV_FIXED -}; - -#undef DIV -#undef DIV_LIST - -/** - * struct k210_gate_params - Parameters for gated clocks - * @off: The offset of the gate from the sysctl base address - * @bit_idx: The index of the bit within the register - */ -struct k210_gate_params { - u8 off; - u8 bit_idx; -}; - -#define GATE_LIST \ - GATE(K210_CLK_CPU, K210_SYSCTL_EN_CENT, 0) \ - GATE(K210_CLK_SRAM0, K210_SYSCTL_EN_CENT, 1) \ - GATE(K210_CLK_SRAM1, K210_SYSCTL_EN_CENT, 2) \ - GATE(K210_CLK_APB0, K210_SYSCTL_EN_CENT, 3) \ - GATE(K210_CLK_APB1, K210_SYSCTL_EN_CENT, 4) \ - GATE(K210_CLK_APB2, K210_SYSCTL_EN_CENT, 5) \ - GATE(K210_CLK_ROM, K210_SYSCTL_EN_PERI, 0) \ - GATE(K210_CLK_DMA, K210_SYSCTL_EN_PERI, 1) \ - GATE(K210_CLK_AI, K210_SYSCTL_EN_PERI, 2) \ - GATE(K210_CLK_DVP, K210_SYSCTL_EN_PERI, 3) \ - GATE(K210_CLK_FFT, K210_SYSCTL_EN_PERI, 4) \ - GATE(K210_CLK_GPIO, K210_SYSCTL_EN_PERI, 5) \ - GATE(K210_CLK_SPI0, K210_SYSCTL_EN_PERI, 6) \ - GATE(K210_CLK_SPI1, K210_SYSCTL_EN_PERI, 7) \ - GATE(K210_CLK_SPI2, K210_SYSCTL_EN_PERI, 8) \ - GATE(K210_CLK_SPI3, K210_SYSCTL_EN_PERI, 9) \ - GATE(K210_CLK_I2S0, K210_SYSCTL_EN_PERI, 10) \ - GATE(K210_CLK_I2S1, K210_SYSCTL_EN_PERI, 11) \ - GATE(K210_CLK_I2S2, K210_SYSCTL_EN_PERI, 12) \ - GATE(K210_CLK_I2C0, K210_SYSCTL_EN_PERI, 13) \ - GATE(K210_CLK_I2C1, K210_SYSCTL_EN_PERI, 14) \ - GATE(K210_CLK_I2C2, K210_SYSCTL_EN_PERI, 15) \ - GATE(K210_CLK_UART1, K210_SYSCTL_EN_PERI, 16) \ - GATE(K210_CLK_UART2, K210_SYSCTL_EN_PERI, 17) \ - GATE(K210_CLK_UART3, K210_SYSCTL_EN_PERI, 18) \ - GATE(K210_CLK_AES, K210_SYSCTL_EN_PERI, 19) \ - GATE(K210_CLK_FPIOA, K210_SYSCTL_EN_PERI, 20) \ - GATE(K210_CLK_TIMER0, K210_SYSCTL_EN_PERI, 21) \ - GATE(K210_CLK_TIMER1, K210_SYSCTL_EN_PERI, 22) \ - GATE(K210_CLK_TIMER2, K210_SYSCTL_EN_PERI, 23) \ - GATE(K210_CLK_WDT0, K210_SYSCTL_EN_PERI, 24) \ - GATE(K210_CLK_WDT1, K210_SYSCTL_EN_PERI, 25) \ - GATE(K210_CLK_SHA, K210_SYSCTL_EN_PERI, 26) \ - GATE(K210_CLK_OTP, K210_SYSCTL_EN_PERI, 27) \ - GATE(K210_CLK_RTC, K210_SYSCTL_EN_PERI, 29) - -#define _GATEIFY(id) K210_CLK_GATE_##id -#define GATEIFY(id) _GATEIFY(id) - -enum k210_gate_id { -#define GATE(id, ...) GATEIFY(id), - GATE_LIST -#undef GATE - K210_CLK_GATE_NONE, -}; - -static const struct k210_gate_params k210_gates[] = { -#define GATE(id, _off, _idx) \ - [GATEIFY(id)] = { \ - .off = (_off), \ - .bit_idx = (_idx), \ - }, - GATE_LIST -#undef GATE -}; - -#undef GATE_LIST - -/* The most parents is PLL2 */ -#define K210_CLK_MAX_PARENTS 3 - -/** - * struct k210_mux_params - Parameters for muxed clocks - * @parents: A list of parent clock ids - * @num_parents: The number of parent clocks - * @off: The offset of the mux from the base sysctl address - * @shift: The offset of the LSB of the mux selector - * @width: The number of bits in the mux selector - */ -struct k210_mux_params { - u8 parents[K210_CLK_MAX_PARENTS]; - u8 num_parents; - u8 off; - u8 shift; - u8 width; -}; - -#define MUX(id, reg, shift, width) \ - MUX_PARENTS(id, reg, shift, width, K210_CLK_IN0, K210_CLK_PLL0) -#define MUX_LIST \ - MUX_PARENTS(K210_CLK_PLL2, K210_SYSCTL_PLL2, 26, 2, \ - K210_CLK_IN0, K210_CLK_PLL0, K210_CLK_PLL1) \ - MUX(K210_CLK_ACLK, K210_SYSCTL_SEL0, 0, 1) \ - MUX(K210_CLK_SPI3, K210_SYSCTL_SEL0, 12, 1) \ - MUX(K210_CLK_TIMER0, K210_SYSCTL_SEL0, 13, 1) \ - MUX(K210_CLK_TIMER1, K210_SYSCTL_SEL0, 14, 1) \ - MUX(K210_CLK_TIMER2, K210_SYSCTL_SEL0, 15, 1) - -#define _MUXIFY(id) K210_CLK_MUX_##id -#define MUXIFY(id) _MUXIFY(id) - -enum k210_mux_id { -#define MUX_PARENTS(id, ...) MUXIFY(id), - MUX_LIST -#undef MUX_PARENTS - K210_CLK_MUX_NONE, -}; - -static const struct k210_mux_params k210_muxes[] = { -#define MUX_PARENTS(id, _off, _shift, _width, ...) \ - [MUXIFY(id)] = { \ - .parents = { __VA_ARGS__ }, \ - .num_parents = __count_args(__VA_ARGS__), \ - .off = (_off), \ - .shift = (_shift), \ - .width = (_width), \ - }, - MUX_LIST -#undef MUX_PARENTS -}; - -#undef MUX -#undef MUX_LIST - -/** - * struct k210_pll_params - K210 PLL parameters - * @off: The offset of the PLL from the base sysctl address - * @shift: The offset of the LSB of the lock status - * @width: The number of bits in the lock status - */ -struct k210_pll_params { - u8 off; - u8 shift; - u8 width; -}; - -static const struct k210_pll_params k210_plls[] = { -#define PLL(_off, _shift, _width) { \ - .off = (_off), \ - .shift = (_shift), \ - .width = (_width), \ -} - [0] = PLL(K210_SYSCTL_PLL0, 0, 2), - [1] = PLL(K210_SYSCTL_PLL1, 8, 1), - [2] = PLL(K210_SYSCTL_PLL2, 16, 1), -#undef PLL -}; - -/** - * enum k210_clk_flags - The type of a K210 clock - * @K210_CLKF_MUX: This clock has a mux and not a static parent - * @K210_CLKF_PLL: This clock is a PLL - */ -enum k210_clk_flags { - K210_CLKF_MUX = BIT(0), - K210_CLKF_PLL = BIT(1), -}; - -/** - * struct k210_clk_params - The parameters defining a K210 clock - * @name: The name of the clock - * @flags: A set of &enum k210_clk_flags defining which fields are valid - * @mux: An &enum k210_mux_id of this clock's mux - * @parent: The clock id of this clock's parent - * @pll: The id of the PLL (if this clock is a PLL) - * @div: An &enum k210_div_id of this clock's divider - * @gate: An &enum k210_gate_id of this clock's gate - */ -struct k210_clk_params { -#if CONFIG_IS_ENABLED(CMD_CLK) - const char *name; -#endif - u8 flags; - union { - u8 parent; - u8 mux; - }; - union { - u8 pll; - struct { - u8 div; - u8 gate; - }; - }; -}; - -static const struct k210_clk_params k210_clks[] = { -#if CONFIG_IS_ENABLED(CMD_CLK) -#define NAME(_name) .name = (_name), -#else -#define NAME(name) -#endif -#define CLK(id, _name, _parent, _div, _gate) \ - [id] = { \ - NAME(_name) \ - .parent = (_parent), \ - .div = (_div), \ - .gate = (_gate), \ - } -#define CLK_MUX(id, _name, _mux, _div, _gate) \ - [id] = { \ - NAME(_name) \ - .flags = K210_CLKF_MUX, \ - .mux = (_mux), \ - .div = (_div), \ - .gate = (_gate), \ - } -#define CLK_PLL(id, _pll, _parent) \ - [id] = { \ - NAME("pll" #_pll) \ - .flags = K210_CLKF_PLL, \ - .parent = (_parent), \ - .pll = (_pll), \ - } -#define CLK_FULL(id, name) \ - CLK_MUX(id, name, MUXIFY(id), DIVIFY(id), GATEIFY(id)) -#define CLK_NOMUX(id, name, parent) \ - CLK(id, name, parent, DIVIFY(id), GATEIFY(id)) -#define CLK_DIV(id, name, parent) \ - CLK(id, name, parent, DIVIFY(id), K210_CLK_GATE_NONE) -#define CLK_GATE(id, name, parent) \ - CLK(id, name, parent, K210_CLK_DIV_NONE, GATEIFY(id)) - CLK_PLL(K210_CLK_PLL0, 0, K210_CLK_IN0), - CLK_PLL(K210_CLK_PLL1, 1, K210_CLK_IN0), - [K210_CLK_PLL2] = { - NAME("pll2") - .flags = K210_CLKF_MUX | K210_CLKF_PLL, - .mux = MUXIFY(K210_CLK_PLL2), - .pll = 2, - }, - CLK_MUX(K210_CLK_ACLK, "aclk", MUXIFY(K210_CLK_ACLK), - DIVIFY(K210_CLK_ACLK), K210_CLK_GATE_NONE), - CLK_FULL(K210_CLK_SPI3, "spi3"), - CLK_FULL(K210_CLK_TIMER0, "timer0"), - CLK_FULL(K210_CLK_TIMER1, "timer1"), - CLK_FULL(K210_CLK_TIMER2, "timer2"), - CLK_NOMUX(K210_CLK_SRAM0, "sram0", K210_CLK_ACLK), - CLK_NOMUX(K210_CLK_SRAM1, "sram1", K210_CLK_ACLK), - CLK_NOMUX(K210_CLK_ROM, "rom", K210_CLK_ACLK), - CLK_NOMUX(K210_CLK_DVP, "dvp", K210_CLK_ACLK), - CLK_NOMUX(K210_CLK_APB0, "apb0", K210_CLK_ACLK), - CLK_NOMUX(K210_CLK_APB1, "apb1", K210_CLK_ACLK), - CLK_NOMUX(K210_CLK_APB2, "apb2", K210_CLK_ACLK), - CLK_NOMUX(K210_CLK_AI, "ai", K210_CLK_PLL1), - CLK_NOMUX(K210_CLK_I2S0, "i2s0", K210_CLK_PLL2), - CLK_NOMUX(K210_CLK_I2S1, "i2s1", K210_CLK_PLL2), - CLK_NOMUX(K210_CLK_I2S2, "i2s2", K210_CLK_PLL2), - CLK_NOMUX(K210_CLK_WDT0, "wdt0", K210_CLK_IN0), - CLK_NOMUX(K210_CLK_WDT1, "wdt1", K210_CLK_IN0), - CLK_NOMUX(K210_CLK_SPI0, "spi0", K210_CLK_PLL0), - CLK_NOMUX(K210_CLK_SPI1, "spi1", K210_CLK_PLL0), - CLK_NOMUX(K210_CLK_SPI2, "spi2", K210_CLK_PLL0), - CLK_NOMUX(K210_CLK_I2C0, "i2c0", K210_CLK_PLL0), - CLK_NOMUX(K210_CLK_I2C1, "i2c1", K210_CLK_PLL0), - CLK_NOMUX(K210_CLK_I2C2, "i2c2", K210_CLK_PLL0), - CLK_DIV(K210_CLK_I2S0_M, "i2s0_m", K210_CLK_PLL2), - CLK_DIV(K210_CLK_I2S1_M, "i2s1_m", K210_CLK_PLL2), - CLK_DIV(K210_CLK_I2S2_M, "i2s2_m", K210_CLK_PLL2), - CLK_DIV(K210_CLK_CLINT, "clint", K210_CLK_ACLK), - CLK_GATE(K210_CLK_CPU, "cpu", K210_CLK_ACLK), - CLK_GATE(K210_CLK_DMA, "dma", K210_CLK_ACLK), - CLK_GATE(K210_CLK_FFT, "fft", K210_CLK_ACLK), - CLK_GATE(K210_CLK_GPIO, "gpio", K210_CLK_APB0), - CLK_GATE(K210_CLK_UART1, "uart1", K210_CLK_APB0), - CLK_GATE(K210_CLK_UART2, "uart2", K210_CLK_APB0), - CLK_GATE(K210_CLK_UART3, "uart3", K210_CLK_APB0), - CLK_GATE(K210_CLK_FPIOA, "fpioa", K210_CLK_APB0), - CLK_GATE(K210_CLK_SHA, "sha", K210_CLK_APB0), - CLK_GATE(K210_CLK_AES, "aes", K210_CLK_APB1), - CLK_GATE(K210_CLK_OTP, "otp", K210_CLK_APB1), - CLK_GATE(K210_CLK_RTC, "rtc", K210_CLK_IN0), -#undef NAME -#undef CLK_PLL -#undef CLK -#undef CLK_FULL -#undef CLK_NOMUX -#undef CLK_DIV -#undef CLK_GATE -#undef CLK_LIST -}; - -#define K210_PLL_CLKR GENMASK(3, 0) -#define K210_PLL_CLKF GENMASK(9, 4) -#define K210_PLL_CLKOD GENMASK(13, 10) /* Output Divider */ -#define K210_PLL_BWADJ GENMASK(19, 14) /* BandWidth Adjust */ -#define K210_PLL_RESET BIT(20) -#define K210_PLL_PWRD BIT(21) /* PoWeReD */ -#define K210_PLL_INTFB BIT(22) /* Internal FeedBack */ -#define K210_PLL_BYPASS BIT(23) -#define K210_PLL_TEST BIT(24) -#define K210_PLL_EN BIT(25) -#define K210_PLL_TEST_EN BIT(26) - -#define K210_PLL_LOCK 0 -#define K210_PLL_CLEAR_SLIP 2 -#define K210_PLL_TEST_OUT 3 - -#ifdef CONFIG_CLK_K210_SET_RATE -static int k210_pll_enable(struct k210_clk_priv *priv, int id); -static int k210_pll_disable(struct k210_clk_priv *priv, int id); -static ulong k210_pll_get_rate(struct k210_clk_priv *priv, int id, ulong rate_in); - -/* - * The PLL included with the Kendryte K210 appears to be a True Circuits, Inc. - * General-Purpose PLL. The logical layout of the PLL with internal feedback is - * approximately the following: - * - * +---------------+ - * |reference clock| - * +---------------+ - * | - * v - * +--+ - * |/r| - * +--+ - * | - * v - * +-------------+ - * |divided clock| - * +-------------+ - * | - * v - * +--------------+ - * |phase detector|<---+ - * +--------------+ | - * | | - * v +--------------+ - * +---+ |feedback clock| - * |VCO| +--------------+ - * +---+ ^ - * | +--+ | - * +--->|/f|---+ - * | +--+ - * v - * +---+ - * |/od| - * +---+ - * | - * v - * +------+ - * |output| - * +------+ - * - * The k210 PLLs have three factors: r, f, and od. Because of the feedback mode, - * the effect of the division by f is to multiply the input frequency. The - * equation for the output rate is - * rate = (rate_in * f) / (r * od). - * Moving knowns to one side of the equation, we get - * rate / rate_in = f / (r * od) - * Rearranging slightly, - * abs_error = abs((rate / rate_in) - (f / (r * od))). - * To get relative, error, we divide by the expected ratio - * error = abs((rate / rate_in) - (f / (r * od))) / (rate / rate_in). - * Simplifying, - * error = abs(1 - f / (r * od)) / (rate / rate_in) - * error = abs(1 - (f * rate_in) / (r * od * rate)) - * Using the constants ratio = rate / rate_in and inv_ratio = rate_in / rate, - * error = abs((f * inv_ratio) / (r * od) - 1) - * This is the error used in evaluating parameters. - * - * r and od are four bits each, while f is six bits. Because r and od are - * multiplied together, instead of the full 256 values possible if both bits - * were used fully, there are only 97 distinct products. Combined with f, there - * are 6208 theoretical settings for the PLL. However, most of these settings - * can be ruled out immediately because they do not have the correct ratio. - * - * In addition to the constraint of approximating the desired ratio, parameters - * must also keep internal pll frequencies within acceptable ranges. The divided - * clock's minimum and maximum frequencies have a ratio of around 128. This - * leaves fairly substantial room to work with, especially since the only - * affected parameter is r. The VCO's minimum and maximum frequency have a ratio - * of 5, which is considerably more restrictive. - * - * The r and od factors are stored in a table. This is to make it easy to find - * the next-largest product. Some products have multiple factorizations, but - * only when one factor has at least a 2.5x ratio to the factors of the other - * factorization. This is because any smaller ratio would not make a difference - * when ensuring the VCO's frequency is within spec. - * - * Throughout the calculation function, fixed point arithmetic is used. Because - * the range of rate and rate_in may be up to 1.75 GHz, or around 2^30, 64-bit - * 32.32 fixed-point numbers are used to represent ratios. In general, to - * implement division, the numerator is first multiplied by 2^32. This gives a - * result where the whole number part is in the upper 32 bits, and the fraction - * is in the lower 32 bits. - * - * In general, rounding is done to the closest integer. This helps find the best - * approximation for the ratio. Rounding in one direction (e.g down) could cause - * the function to miss a better ratio with one of the parameters increased by - * one. - */ - -/* - * The factors table was generated with the following python code: - * - * def p(x, y): - * return (1.0*x/y > 2.5) or (1.0*y/x > 2.5) - * - * factors = {} - * for i in range(1, 17): - * for j in range(1, 17): - * fs = factors.get(i*j) or [] - * if fs == [] or all([ - * (p(i, x) and p(i, y)) or (p(j, x) and p(j, y)) - * for (x, y) in fs]): - * fs.append((i, j)) - * factors[i*j] = fs - * - * for k, l in sorted(factors.items()): - * for v in l: - * print("PACK(%s, %s)," % v) - */ -#define PACK(r, od) (((((r) - 1) & 0xF) << 4) | (((od) - 1) & 0xF)) -#define UNPACK_R(val) ((((val) >> 4) & 0xF) + 1) -#define UNPACK_OD(val) (((val) & 0xF) + 1) -static const u8 factors[] = { - PACK(1, 1), - PACK(1, 2), - PACK(1, 3), - PACK(1, 4), - PACK(1, 5), - PACK(1, 6), - PACK(1, 7), - PACK(1, 8), - PACK(1, 9), - PACK(3, 3), - PACK(1, 10), - PACK(1, 11), - PACK(1, 12), - PACK(3, 4), - PACK(1, 13), - PACK(1, 14), - PACK(1, 15), - PACK(3, 5), - PACK(1, 16), - PACK(4, 4), - PACK(2, 9), - PACK(2, 10), - PACK(3, 7), - PACK(2, 11), - PACK(2, 12), - PACK(5, 5), - PACK(2, 13), - PACK(3, 9), - PACK(2, 14), - PACK(2, 15), - PACK(2, 16), - PACK(3, 11), - PACK(5, 7), - PACK(3, 12), - PACK(3, 13), - PACK(4, 10), - PACK(3, 14), - PACK(4, 11), - PACK(3, 15), - PACK(3, 16), - PACK(7, 7), - PACK(5, 10), - PACK(4, 13), - PACK(6, 9), - PACK(5, 11), - PACK(4, 14), - PACK(4, 15), - PACK(7, 9), - PACK(4, 16), - PACK(5, 13), - PACK(6, 11), - PACK(5, 14), - PACK(6, 12), - PACK(5, 15), - PACK(7, 11), - PACK(6, 13), - PACK(5, 16), - PACK(9, 9), - PACK(6, 14), - PACK(8, 11), - PACK(6, 15), - PACK(7, 13), - PACK(6, 16), - PACK(7, 14), - PACK(9, 11), - PACK(10, 10), - PACK(8, 13), - PACK(7, 15), - PACK(9, 12), - PACK(10, 11), - PACK(7, 16), - PACK(9, 13), - PACK(8, 15), - PACK(11, 11), - PACK(9, 14), - PACK(8, 16), - PACK(10, 13), - PACK(11, 12), - PACK(9, 15), - PACK(10, 14), - PACK(11, 13), - PACK(9, 16), - PACK(10, 15), - PACK(11, 14), - PACK(12, 13), - PACK(10, 16), - PACK(11, 15), - PACK(12, 14), - PACK(13, 13), - PACK(11, 16), - PACK(12, 15), - PACK(13, 14), - PACK(12, 16), - PACK(13, 15), - PACK(14, 14), - PACK(13, 16), - PACK(14, 15), - PACK(14, 16), - PACK(15, 15), - PACK(15, 16), - PACK(16, 16), -}; - -TEST_STATIC int k210_pll_calc_config(u32 rate, u32 rate_in, - struct k210_pll_config *best) -{ - int i; - s64 error, best_error; - u64 ratio, inv_ratio; /* fixed point 32.32 ratio of the rates */ - u64 max_r; - u64 r, f, od; - - /* - * Can't go over 1.75 GHz or under 21.25 MHz due to limitations on the - * VCO frequency. These are not the same limits as below because od can - * reduce the output frequency by 16. - */ - if (rate > 1750000000 || rate < 21250000) - return -EINVAL; - - /* Similar restrictions on the input rate */ - if (rate_in > 1750000000 || rate_in < 13300000) - return -EINVAL; - - ratio = DIV_ROUND_CLOSEST_ULL((u64)rate << 32, rate_in); - inv_ratio = DIV_ROUND_CLOSEST_ULL((u64)rate_in << 32, rate); - /* Can't increase by more than 64 or reduce by more than 256 */ - if (rate > rate_in && ratio > (64ULL << 32)) - return -EINVAL; - else if (rate <= rate_in && inv_ratio > (256ULL << 32)) - return -EINVAL; - - /* - * The divided clock (rate_in / r) must stay between 1.75 GHz and 13.3 - * MHz. There is no minimum, since the only way to get a higher input - * clock than 26 MHz is to use a clock generated by a PLL. Because PLLs - * cannot output frequencies greater than 1.75 GHz, the minimum would - * never be greater than one. - */ - max_r = DIV_ROUND_DOWN_ULL(rate_in, 13300000); - - /* Variables get immediately incremented, so start at -1th iteration */ - i = -1; - f = 0; - r = 0; - od = 0; - best_error = S64_MAX; - error = best_error; - /* do-while here so we always try at least one ratio */ - do { - /* - * Whether we swapped r and od while enforcing frequency limits - */ - bool swapped = false; - /* - * Whether the intermediate frequencies are out-of-spec - */ - bool out_of_spec; - u64 last_od = od; - u64 last_r = r; - - /* - * Try the next largest value for f (or r and od) and - * recalculate the other parameters based on that - */ - if (rate > rate_in) { - /* - * Skip factors of the same product if we already tried - * out that product - */ - do { - i++; - r = UNPACK_R(factors[i]); - od = UNPACK_OD(factors[i]); - } while (i + 1 < ARRAY_SIZE(factors) && - r * od == last_r * last_od); - - /* Round close */ - f = (r * od * ratio + BIT(31)) >> 32; - if (f > 64) - f = 64; - } else { - u64 tmp = ++f * inv_ratio; - bool round_up = !!(tmp & BIT(31)); - u32 goal = (tmp >> 32) + round_up; - u32 err, last_err; - - /* Get the next r/od pair in factors */ - while (r * od < goal && i + 1 < ARRAY_SIZE(factors)) { - i++; - r = UNPACK_R(factors[i]); - od = UNPACK_OD(factors[i]); - } - - /* - * This is a case of double rounding. If we rounded up - * above, we need to round down (in cases of ties) here. - * This prevents off-by-one errors resulting from - * choosing X+2 over X when X.Y rounds up to X+1 and - * there is no r * od = X+1. For the converse, when X.Y - * is rounded down to X, we should choose X+1 over X-1. - */ - err = abs(r * od - goal); - last_err = abs(last_r * last_od - goal); - if (last_err < err || (round_up && last_err == err)) { - i--; - r = last_r; - od = last_od; - } - } - - /* - * Enforce limits on internal clock frequencies. If we - * aren't in spec, try swapping r and od. If everything is - * in-spec, calculate the relative error. - */ -again: - out_of_spec = false; - if (r > max_r) { - out_of_spec = true; - } else { - /* - * There is no way to only divide once; we need - * to examine the frequency with and without the - * effect of od. - */ - u64 vco = DIV_ROUND_CLOSEST_ULL(rate_in * f, r); - - if (vco > 1750000000 || vco < 340000000) - out_of_spec = true; - } - - if (out_of_spec) { - u64 new_r, new_od; - - if (!swapped) { - u64 tmp = r; - - r = od; - od = tmp; - swapped = true; - goto again; - } - - /* - * Try looking ahead to see if there are additional - * factors for the same product. - */ - if (i + 1 < ARRAY_SIZE(factors)) { - i++; - new_r = UNPACK_R(factors[i]); - new_od = UNPACK_OD(factors[i]); - if (r * od == new_r * new_od) { - r = new_r; - od = new_od; - swapped = false; - goto again; - } - i--; - } - - /* - * Try looking back to see if there is a worse ratio - * that we could try anyway - */ - while (i > 0) { - i--; - new_r = UNPACK_R(factors[i]); - new_od = UNPACK_OD(factors[i]); - /* - * Don't loop over factors for the same product - * to avoid getting stuck because of the above - * clause - */ - if (r * od != new_r * new_od) { - if (new_r * new_od > last_r * last_od) { - r = new_r; - od = new_od; - swapped = false; - goto again; - } - break; - } - } - - /* We ran out of things to try */ - continue; - } - - error = DIV_ROUND_CLOSEST_ULL(f * inv_ratio, r * od); - /* The lower 16 bits are spurious */ - error = abs((error - BIT(32))) >> 16; - - if (error < best_error) { - best->r = r; - best->f = f; - best->od = od; - best_error = error; - } - } while (f < 64 && i + 1 < ARRAY_SIZE(factors) && error != 0); - - log_debug("best error %lld\n", best_error); - if (best_error == S64_MAX) - return -EINVAL; - - return 0; -} - -static ulong k210_pll_set_rate(struct k210_clk_priv *priv, int id, ulong rate, - ulong rate_in) -{ - int err; - const struct k210_pll_params *pll = &k210_plls[id]; - struct k210_pll_config config = {}; - u32 reg; - ulong calc_rate; - - err = k210_pll_calc_config(rate, rate_in, &config); - if (err) - return err; - log_debug("Got r=%u f=%u od=%u\n", config.r, config.f, config.od); - - /* Don't bother setting the rate if we're already at that rate */ - calc_rate = DIV_ROUND_DOWN_ULL(((u64)rate_in) * config.f, - config.r * config.od); - if (calc_rate == k210_pll_get_rate(priv, id, rate)) - return calc_rate; - - k210_pll_disable(priv, id); - - reg = readl(priv->base + pll->off); - reg &= ~K210_PLL_CLKR - & ~K210_PLL_CLKF - & ~K210_PLL_CLKOD - & ~K210_PLL_BWADJ; - reg |= FIELD_PREP(K210_PLL_CLKR, config.r - 1) - | FIELD_PREP(K210_PLL_CLKF, config.f - 1) - | FIELD_PREP(K210_PLL_CLKOD, config.od - 1) - | FIELD_PREP(K210_PLL_BWADJ, config.f - 1); - writel(reg, priv->base + pll->off); - - k210_pll_enable(priv, id); - - serial_setbrg(); - return k210_pll_get_rate(priv, id, rate); -} -#else -static ulong k210_pll_set_rate(struct k210_clk_priv *priv, int id, ulong rate, - ulong rate_in) -{ - return -ENOSYS; -} -#endif /* CONFIG_CLK_K210_SET_RATE */ - -static ulong k210_pll_get_rate(struct k210_clk_priv *priv, int id, - ulong rate_in) -{ - u64 r, f, od; - u32 reg = readl(priv->base + k210_plls[id].off); - - if (reg & K210_PLL_BYPASS) - return rate_in; - - if (!(reg & K210_PLL_PWRD)) - return 0; - - r = FIELD_GET(K210_PLL_CLKR, reg) + 1; - f = FIELD_GET(K210_PLL_CLKF, reg) + 1; - od = FIELD_GET(K210_PLL_CLKOD, reg) + 1; - - return DIV_ROUND_DOWN_ULL(((u64)rate_in) * f, r * od); -} - -/* - * Wait for the PLL to be locked. If the PLL is not locked, try clearing the - * slip before retrying - */ -static void k210_pll_waitfor_lock(struct k210_clk_priv *priv, int id) -{ - const struct k210_pll_params *pll = &k210_plls[id]; - u32 mask = (BIT(pll->width) - 1) << pll->shift; - - while (true) { - u32 reg = readl(priv->base + K210_SYSCTL_PLL_LOCK); - - if ((reg & mask) == mask) - break; - - reg |= BIT(pll->shift + K210_PLL_CLEAR_SLIP); - writel(reg, priv->base + K210_SYSCTL_PLL_LOCK); - } -} - -static bool k210_pll_enabled(u32 reg) -{ - return (reg & K210_PLL_PWRD) && (reg & K210_PLL_EN) && - !(reg & K210_PLL_RESET); -} - -/* Adapted from sysctl_pll_enable */ -static int k210_pll_enable(struct k210_clk_priv *priv, int id) -{ - const struct k210_pll_params *pll = &k210_plls[id]; - u32 reg = readl(priv->base + pll->off); - - if (k210_pll_enabled(reg)) - return 0; - - reg |= K210_PLL_PWRD; - writel(reg, priv->base + pll->off); - - /* Ensure reset is low before asserting it */ - reg &= ~K210_PLL_RESET; - writel(reg, priv->base + pll->off); - reg |= K210_PLL_RESET; - writel(reg, priv->base + pll->off); - nop(); - nop(); - reg &= ~K210_PLL_RESET; - writel(reg, priv->base + pll->off); - - k210_pll_waitfor_lock(priv, id); - - reg &= ~K210_PLL_BYPASS; - reg |= K210_PLL_EN; - writel(reg, priv->base + pll->off); - - return 0; -} - -static int k210_pll_disable(struct k210_clk_priv *priv, int id) -{ - const struct k210_pll_params *pll = &k210_plls[id]; - u32 reg = readl(priv->base + pll->off); - - /* - * Bypassing before powering off is important so child clocks don't stop - * working. This is especially important for pll0, the indirect parent - * of the cpu clock. - */ - reg |= K210_PLL_BYPASS; - writel(reg, priv->base + pll->off); - - reg &= ~K210_PLL_PWRD; - reg &= ~K210_PLL_EN; - writel(reg, priv->base + pll->off); - return 0; -} - -static u32 k210_clk_readl(struct k210_clk_priv *priv, u8 off, u8 shift, - u8 width) -{ - u32 reg = readl(priv->base + off); - - return (reg >> shift) & (BIT(width) - 1); -} - -static void k210_clk_writel(struct k210_clk_priv *priv, u8 off, u8 shift, - u8 width, u32 val) -{ - u32 reg = readl(priv->base + off); - u32 mask = (BIT(width) - 1) << shift; - - reg &= ~mask; - reg |= mask & (val << shift); - writel(reg, priv->base + off); -} - -static int k210_clk_get_parent(struct k210_clk_priv *priv, int id) -{ - u32 sel; - const struct k210_mux_params *mux; - - if (!(k210_clks[id].flags & K210_CLKF_MUX)) - return k210_clks[id].parent; - mux = &k210_muxes[k210_clks[id].mux]; - - sel = k210_clk_readl(priv, mux->off, mux->shift, mux->width); - assert(sel < mux->num_parents); - return mux->parents[sel]; -} - -static ulong do_k210_clk_get_rate(struct k210_clk_priv *priv, int id) -{ - int parent; - u32 val; - ulong parent_rate; - const struct k210_div_params *div; - - if (id == K210_CLK_IN0) - return clk_get_rate(&priv->in0); - - parent = k210_clk_get_parent(priv, id); - parent_rate = do_k210_clk_get_rate(priv, parent); - if (IS_ERR_VALUE(parent_rate)) - return parent_rate; - - if (k210_clks[id].flags & K210_CLKF_PLL) - return k210_pll_get_rate(priv, k210_clks[id].pll, parent_rate); - - if (k210_clks[id].div == K210_CLK_DIV_NONE) - return parent_rate; - div = &k210_divs[k210_clks[id].div]; - - if (div->type == K210_DIV_FIXED) - return parent_rate / div->div; - - val = k210_clk_readl(priv, div->off, div->shift, div->width); - switch (div->type) { - case K210_DIV_ONE: - return parent_rate / (val + 1); - case K210_DIV_EVEN: - return parent_rate / 2 / (val + 1); - case K210_DIV_POWER: - /* This is ACLK, which has no divider on IN0 */ - if (parent == K210_CLK_IN0) - return parent_rate; - return parent_rate / (2 << val); - default: - assert(false); - return -EINVAL; - }; -} - -static ulong k210_clk_get_rate(struct clk *clk) -{ - return do_k210_clk_get_rate(dev_get_priv(clk->dev), clk->id); -} - -static int do_k210_clk_set_parent(struct k210_clk_priv *priv, int id, int new) -{ - int i; - const struct k210_mux_params *mux; - - if (!(k210_clks[id].flags & K210_CLKF_MUX)) - return -ENOSYS; - mux = &k210_muxes[k210_clks[id].mux]; - - for (i = 0; i < mux->num_parents; i++) { - if (mux->parents[i] == new) { - k210_clk_writel(priv, mux->off, mux->shift, mux->width, - i); - return 0; - } - } - return -EINVAL; -} - -static int k210_clk_set_parent(struct clk *clk, struct clk *parent) -{ - return do_k210_clk_set_parent(dev_get_priv(clk->dev), clk->id, - parent->id); -} - -static ulong k210_clk_set_rate(struct clk *clk, unsigned long rate) -{ - int parent, ret, err; - ulong rate_in, val; - const struct k210_div_params *div; - struct k210_clk_priv *priv = dev_get_priv(clk->dev); - - if (clk->id == K210_CLK_IN0) - return clk_set_rate(&priv->in0, rate); - - parent = k210_clk_get_parent(priv, clk->id); - rate_in = do_k210_clk_get_rate(priv, parent); - if (IS_ERR_VALUE(rate_in)) - return rate_in; - - log_debug("id=%ld rate=%lu rate_in=%lu\n", clk->id, rate, rate_in); - - if (clk->id == K210_CLK_PLL0) { - /* Bypass ACLK so the CPU keeps going */ - ret = do_k210_clk_set_parent(priv, K210_CLK_ACLK, K210_CLK_IN0); - if (ret) - return ret; - } else if (clk->id == K210_CLK_PLL1 && gd->flags & GD_FLG_RELOC) { - /* - * We can't bypass the AI clock like we can ACLK, and after - * relocation we are using the AI ram. - */ - return -EPERM; - } - - if (k210_clks[clk->id].flags & K210_CLKF_PLL) { - ret = k210_pll_set_rate(priv, k210_clks[clk->id].pll, rate, - rate_in); - if (!IS_ERR_VALUE(ret) && clk->id == K210_CLK_PLL0) { - /* - * This may have the side effect of reparenting ACLK, - * but I don't really want to keep track of what the old - * parent was. - */ - err = do_k210_clk_set_parent(priv, K210_CLK_ACLK, - K210_CLK_PLL0); - if (err) - return err; - } - return ret; - } - - if (k210_clks[clk->id].div == K210_CLK_DIV_NONE) - return -ENOSYS; - div = &k210_divs[k210_clks[clk->id].div]; - - switch (div->type) { - case K210_DIV_ONE: - val = DIV_ROUND_CLOSEST_ULL((u64)rate_in, rate); - val = val ? val - 1 : 0; - break; - case K210_DIV_EVEN: - val = DIV_ROUND_CLOSEST_ULL((u64)rate_in, 2 * rate); - break; - case K210_DIV_POWER: - /* This is ACLK, which has no divider on IN0 */ - if (parent == K210_CLK_IN0) - return -ENOSYS; - - val = DIV_ROUND_CLOSEST_ULL((u64)rate_in, rate); - val = __ffs(val); - break; - default: - assert(false); - return -EINVAL; - }; - - val = val ? val - 1 : 0; - k210_clk_writel(priv, div->off, div->shift, div->width, val); - return do_k210_clk_get_rate(priv, clk->id); -} - -static int k210_clk_endisable(struct k210_clk_priv *priv, int id, bool enable) -{ - int parent = k210_clk_get_parent(priv, id); - const struct k210_gate_params *gate; - - if (id == K210_CLK_IN0) { - if (enable) - return clk_enable(&priv->in0); - else - return clk_disable(&priv->in0); - } - - /* Only recursively enable clocks since we don't track refcounts */ - if (enable) { - int ret = k210_clk_endisable(priv, parent, true); - - if (ret && ret != -ENOSYS) - return ret; - } - - if (k210_clks[id].flags & K210_CLKF_PLL) { - if (enable) - return k210_pll_enable(priv, k210_clks[id].pll); - else - return k210_pll_disable(priv, k210_clks[id].pll); - } - - if (k210_clks[id].gate == K210_CLK_GATE_NONE) - return -ENOSYS; - gate = &k210_gates[k210_clks[id].gate]; - - k210_clk_writel(priv, gate->off, gate->bit_idx, 1, enable); - return 0; -} - -static int k210_clk_enable(struct clk *clk) -{ - return k210_clk_endisable(dev_get_priv(clk->dev), clk->id, true); -} - -static int k210_clk_disable(struct clk *clk) -{ - return k210_clk_endisable(dev_get_priv(clk->dev), clk->id, false); -} - -static int k210_clk_request(struct clk *clk) -{ - if (clk->id >= ARRAY_SIZE(k210_clks)) - return -EINVAL; - return 0; -} - -static const struct clk_ops k210_clk_ops = { - .request = k210_clk_request, - .set_rate = k210_clk_set_rate, - .get_rate = k210_clk_get_rate, - .set_parent = k210_clk_set_parent, - .enable = k210_clk_enable, - .disable = k210_clk_disable, -}; - -static int k210_clk_probe(struct udevice *dev) -{ - int ret; - struct k210_clk_priv *priv = dev_get_priv(dev); - - priv->base = dev_read_addr_ptr(dev_get_parent(dev)); - if (!priv->base) - return -EINVAL; - - ret = clk_get_by_index(dev, 0, &priv->in0); - if (ret) - return ret; - - /* - * Force setting defaults, even before relocation. This is so we can - * set the clock rate for PLL1 before we relocate into aisram. - */ - if (!(gd->flags & GD_FLG_RELOC)) - clk_set_defaults(dev, CLK_DEFAULTS_POST_FORCE); - - return 0; -} - -static const struct udevice_id k210_clk_ids[] = { - { .compatible = "kendryte,k210-clk" }, - { }, -}; - -U_BOOT_DRIVER(k210_clk) = { - .name = "k210_clk", - .id = UCLASS_CLK, - .of_match = k210_clk_ids, - .ops = &k210_clk_ops, - .probe = k210_clk_probe, - .priv_auto = sizeof(struct k210_clk_priv), -}; - -#if CONFIG_IS_ENABLED(CMD_CLK) -static char show_enabled(struct k210_clk_priv *priv, int id) -{ - bool enabled; - - if (k210_clks[id].flags & K210_CLKF_PLL) { - const struct k210_pll_params *pll = - &k210_plls[k210_clks[id].pll]; - - enabled = k210_pll_enabled(readl(priv->base + pll->off)); - } else if (k210_clks[id].gate == K210_CLK_GATE_NONE) { - return '-'; - } else { - const struct k210_gate_params *gate = - &k210_gates[k210_clks[id].gate]; - - enabled = k210_clk_readl(priv, gate->off, gate->bit_idx, 1); - } - - return enabled ? 'y' : 'n'; -} - -static void show_clks(struct k210_clk_priv *priv, int id, int depth) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(k210_clks); i++) { - if (k210_clk_get_parent(priv, i) != id) - continue; - - printf(" %-9lu %-7c %*s%s\n", do_k210_clk_get_rate(priv, i), - show_enabled(priv, i), depth * 4, "", - k210_clks[i].name); - - show_clks(priv, i, depth + 1); - } -} - -int soc_clk_dump(void) -{ - int ret; - struct udevice *dev; - struct k210_clk_priv *priv; - - ret = uclass_get_device_by_driver(UCLASS_CLK, DM_DRIVER_GET(k210_clk), - &dev); - if (ret) - return ret; - priv = dev_get_priv(dev); - - puts(" Rate Enabled Name\n"); - puts("------------------------\n"); - printf(" %-9lu %-7c %*s%s\n", clk_get_rate(&priv->in0), 'y', 0, "", - priv->in0.dev->name); - show_clks(priv, K210_CLK_IN0, 1); - return 0; -} -#endif diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index fd736a7f64..df37c32033 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -18,7 +18,7 @@ obj-$(CONFIG_PINCTRL_SANDBOX) += pinctrl-sandbox.o obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/ obj-$(CONFIG_PINCTRL_PIC32) += pinctrl_pic32.o obj-$(CONFIG_PINCTRL_EXYNOS) += exynos/ -obj-$(CONFIG_PINCTRL_K210) += pinctrl-kendryte.o +obj-$(CONFIG_PINCTRL_K210) += pinctrl-k210.o obj-$(CONFIG_PINCTRL_MESON) += meson/ obj-$(CONFIG_PINCTRL_MTK) += mediatek/ obj-$(CONFIG_PINCTRL_MSCC) += mscc/ diff --git a/drivers/pinctrl/pinctrl-k210.c b/drivers/pinctrl/pinctrl-k210.c new file mode 100644 index 0000000000..bb5153c673 --- /dev/null +++ b/drivers/pinctrl/pinctrl-k210.c @@ -0,0 +1,740 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Sean Anderson + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The K210 only implements 8 drive levels, even though there is register space + * for 16 + */ +#define K210_PC_DRIVE_MASK GENMASK(11, 8) +#define K210_PC_DRIVE_SHIFT 8 +#define K210_PC_DRIVE_0 (0 << K210_PC_DRIVE_SHIFT) +#define K210_PC_DRIVE_1 (1 << K210_PC_DRIVE_SHIFT) +#define K210_PC_DRIVE_2 (2 << K210_PC_DRIVE_SHIFT) +#define K210_PC_DRIVE_3 (3 << K210_PC_DRIVE_SHIFT) +#define K210_PC_DRIVE_4 (4 << K210_PC_DRIVE_SHIFT) +#define K210_PC_DRIVE_5 (5 << K210_PC_DRIVE_SHIFT) +#define K210_PC_DRIVE_6 (6 << K210_PC_DRIVE_SHIFT) +#define K210_PC_DRIVE_7 (7 << K210_PC_DRIVE_SHIFT) +#define K210_PC_DRIVE_MAX 7 + +#define K210_PC_MODE_MASK GENMASK(23, 12) +/* + * output enabled == PC_OE & (PC_OE_INV ^ FUNCTION_OE) where FUNCTION_OE is a + * physical signal from the function + */ +#define K210_PC_OE BIT(12) /* Output Enable */ +#define K210_PC_OE_INV BIT(13) /* INVert function-controlled Output Enable */ +#define K210_PC_DO_OE BIT(14) /* set Data Out to the Output Enable signal */ +#define K210_PC_DO_INV BIT(15) /* INVert final Data Output */ +#define K210_PC_PU BIT(16) /* Pull Up */ +#define K210_PC_PD BIT(17) /* Pull Down */ +/* Strong pull up not implemented on K210 */ +#define K210_PC_SL BIT(19) /* reduce SLew rate to prevent overshoot */ +/* Same semantics as OE above */ +#define K210_PC_IE BIT(20) /* Input Enable */ +#define K210_PC_IE_INV BIT(21) /* INVert function-controlled Input Enable */ +#define K210_PC_DI_INV BIT(22) /* INVert Data Input */ +#define K210_PC_ST BIT(23) /* Schmitt Trigger */ +#define K210_PC_DI BIT(31) /* raw Data Input */ +#define K210_PC_BIAS_MASK (K210_PC_PU & K210_PC_PD) + +#define K210_PC_MODE_IN (K210_PC_IE | K210_PC_ST) +#define K210_PC_MODE_OUT (K210_PC_DRIVE_7 | K210_PC_OE) +#define K210_PC_MODE_I2C (K210_PC_MODE_IN | K210_PC_SL | K210_PC_OE | \ + K210_PC_PU) +#define K210_PC_MODE_SCCB (K210_PC_MODE_I2C | K210_PC_OE_INV | K210_PC_IE_INV) +#define K210_PC_MODE_SPI (K210_PC_MODE_IN | K210_PC_IE_INV | \ + K210_PC_MODE_OUT | K210_PC_OE_INV) +#define K210_PC_MODE_GPIO (K210_PC_MODE_IN | K210_PC_MODE_OUT) + +#define K210_PG_FUNC GENMASK(7, 0) +#define K210_PG_DO BIT(8) +#define K210_PG_PIN GENMASK(22, 16) + +#define PIN_CONFIG_OUTPUT_INVERT (PIN_CONFIG_END + 1) +#define PIN_CONFIG_INPUT_INVERT (PIN_CONFIG_END + 2) + +struct k210_fpioa { + u32 pins[48]; + u32 tie_en[8]; + u32 tie_val[8]; +}; + +struct k210_pc_priv { + struct clk clk; + struct k210_fpioa __iomem *fpioa; /* FPIOA register */ + struct regmap *sysctl; /* Sysctl regmap */ + u32 power_offset; /* Power bank register offset */ +}; + +#ifdef CONFIG_CMD_PINMUX +static const char k210_pc_pin_names[][6] = { +#define PIN(i) \ + [i] = "IO_" #i + PIN(0), + PIN(1), + PIN(2), + PIN(3), + PIN(4), + PIN(5), + PIN(6), + PIN(7), + PIN(8), + PIN(9), + PIN(10), + PIN(11), + PIN(12), + PIN(13), + PIN(14), + PIN(15), + PIN(16), + PIN(17), + PIN(18), + PIN(19), + PIN(20), + PIN(21), + PIN(22), + PIN(23), + PIN(24), + PIN(25), + PIN(26), + PIN(27), + PIN(28), + PIN(29), + PIN(30), + PIN(31), + PIN(32), + PIN(33), + PIN(34), + PIN(35), + PIN(36), + PIN(37), + PIN(38), + PIN(39), + PIN(40), + PIN(41), + PIN(42), + PIN(43), + PIN(44), + PIN(45), + PIN(46), + PIN(47), +#undef PIN +}; + +static int k210_pc_get_pins_count(struct udevice *dev) +{ + return ARRAY_SIZE(k210_pc_pin_names); +}; + +static const char *k210_pc_get_pin_name(struct udevice *dev, unsigned selector) +{ + return k210_pc_pin_names[selector]; +} +#endif /* CONFIG_CMD_PINMUX */ + +/* These are just power domains */ +static const char k210_pc_group_names[][3] = { + [0] = "A0", + [1] = "A1", + [2] = "A2", + [3] = "B3", + [4] = "B4", + [5] = "B5", + [6] = "C6", + [7] = "C7", +}; + +static int k210_pc_get_groups_count(struct udevice *dev) +{ + return ARRAY_SIZE(k210_pc_group_names); +} + +static const char *k210_pc_get_group_name(struct udevice *dev, + unsigned selector) +{ + return k210_pc_group_names[selector]; +} + +enum k210_pc_mode_id { + K210_PC_DEFAULT_DISABLED, + K210_PC_DEFAULT_IN, + K210_PC_DEFAULT_IN_TIE, + K210_PC_DEFAULT_OUT, + K210_PC_DEFAULT_I2C, + K210_PC_DEFAULT_SCCB, + K210_PC_DEFAULT_SPI, + K210_PC_DEFAULT_GPIO, + K210_PC_DEFAULT_INT13, +}; + +static const u32 k210_pc_mode_id_to_mode[] = { +#define DEFAULT(mode) \ + [K210_PC_DEFAULT_##mode] = K210_PC_MODE_##mode + [K210_PC_DEFAULT_DISABLED] = 0, + DEFAULT(IN), + [K210_PC_DEFAULT_IN_TIE] = K210_PC_MODE_IN, + DEFAULT(OUT), + DEFAULT(I2C), + DEFAULT(SCCB), + DEFAULT(SPI), + DEFAULT(GPIO), + [K210_PC_DEFAULT_INT13] = K210_PC_MODE_IN | K210_PC_PU, +#undef DEFAULT +}; + +/* This saves around 2K vs having a pointer+mode */ +struct k210_pcf_info { +#ifdef CONFIG_CMD_PINMUX + char name[15]; +#endif + u8 mode_id; +}; + +static const struct k210_pcf_info k210_pcf_infos[] = { +#ifdef CONFIG_CMD_PINMUX +#define FUNC(id, mode) \ + [K210_PCF_##id] = { \ + .name = #id, \ + .mode_id = K210_PC_DEFAULT_##mode \ + } +#else +#define FUNC(id, mode) \ + [K210_PCF_##id] = { \ + .mode_id = K210_PC_DEFAULT_##mode \ + } +#endif + FUNC(JTAG_TCLK, IN), + FUNC(JTAG_TDI, IN), + FUNC(JTAG_TMS, IN), + FUNC(JTAG_TDO, OUT), + FUNC(SPI0_D0, SPI), + FUNC(SPI0_D1, SPI), + FUNC(SPI0_D2, SPI), + FUNC(SPI0_D3, SPI), + FUNC(SPI0_D4, SPI), + FUNC(SPI0_D5, SPI), + FUNC(SPI0_D6, SPI), + FUNC(SPI0_D7, SPI), + FUNC(SPI0_SS0, OUT), + FUNC(SPI0_SS1, OUT), + FUNC(SPI0_SS2, OUT), + FUNC(SPI0_SS3, OUT), + FUNC(SPI0_ARB, IN_TIE), + FUNC(SPI0_SCLK, OUT), + FUNC(UARTHS_RX, IN), + FUNC(UARTHS_TX, OUT), + FUNC(RESV6, IN), + FUNC(RESV7, IN), + FUNC(CLK_SPI1, OUT), + FUNC(CLK_I2C1, OUT), + FUNC(GPIOHS0, GPIO), + FUNC(GPIOHS1, GPIO), + FUNC(GPIOHS2, GPIO), + FUNC(GPIOHS3, GPIO), + FUNC(GPIOHS4, GPIO), + FUNC(GPIOHS5, GPIO), + FUNC(GPIOHS6, GPIO), + FUNC(GPIOHS7, GPIO), + FUNC(GPIOHS8, GPIO), + FUNC(GPIOHS9, GPIO), + FUNC(GPIOHS10, GPIO), + FUNC(GPIOHS11, GPIO), + FUNC(GPIOHS12, GPIO), + FUNC(GPIOHS13, GPIO), + FUNC(GPIOHS14, GPIO), + FUNC(GPIOHS15, GPIO), + FUNC(GPIOHS16, GPIO), + FUNC(GPIOHS17, GPIO), + FUNC(GPIOHS18, GPIO), + FUNC(GPIOHS19, GPIO), + FUNC(GPIOHS20, GPIO), + FUNC(GPIOHS21, GPIO), + FUNC(GPIOHS22, GPIO), + FUNC(GPIOHS23, GPIO), + FUNC(GPIOHS24, GPIO), + FUNC(GPIOHS25, GPIO), + FUNC(GPIOHS26, GPIO), + FUNC(GPIOHS27, GPIO), + FUNC(GPIOHS28, GPIO), + FUNC(GPIOHS29, GPIO), + FUNC(GPIOHS30, GPIO), + FUNC(GPIOHS31, GPIO), + FUNC(GPIO0, GPIO), + FUNC(GPIO1, GPIO), + FUNC(GPIO2, GPIO), + FUNC(GPIO3, GPIO), + FUNC(GPIO4, GPIO), + FUNC(GPIO5, GPIO), + FUNC(GPIO6, GPIO), + FUNC(GPIO7, GPIO), + FUNC(UART1_RX, IN), + FUNC(UART1_TX, OUT), + FUNC(UART2_RX, IN), + FUNC(UART2_TX, OUT), + FUNC(UART3_RX, IN), + FUNC(UART3_TX, OUT), + FUNC(SPI1_D0, SPI), + FUNC(SPI1_D1, SPI), + FUNC(SPI1_D2, SPI), + FUNC(SPI1_D3, SPI), + FUNC(SPI1_D4, SPI), + FUNC(SPI1_D5, SPI), + FUNC(SPI1_D6, SPI), + FUNC(SPI1_D7, SPI), + FUNC(SPI1_SS0, OUT), + FUNC(SPI1_SS1, OUT), + FUNC(SPI1_SS2, OUT), + FUNC(SPI1_SS3, OUT), + FUNC(SPI1_ARB, IN_TIE), + FUNC(SPI1_SCLK, OUT), + FUNC(SPI2_D0, SPI), + FUNC(SPI2_SS, IN), + FUNC(SPI2_SCLK, IN), + FUNC(I2S0_MCLK, OUT), + FUNC(I2S0_SCLK, OUT), + FUNC(I2S0_WS, OUT), + FUNC(I2S0_IN_D0, IN), + FUNC(I2S0_IN_D1, IN), + FUNC(I2S0_IN_D2, IN), + FUNC(I2S0_IN_D3, IN), + FUNC(I2S0_OUT_D0, OUT), + FUNC(I2S0_OUT_D1, OUT), + FUNC(I2S0_OUT_D2, OUT), + FUNC(I2S0_OUT_D3, OUT), + FUNC(I2S1_MCLK, OUT), + FUNC(I2S1_SCLK, OUT), + FUNC(I2S1_WS, OUT), + FUNC(I2S1_IN_D0, IN), + FUNC(I2S1_IN_D1, IN), + FUNC(I2S1_IN_D2, IN), + FUNC(I2S1_IN_D3, IN), + FUNC(I2S1_OUT_D0, OUT), + FUNC(I2S1_OUT_D1, OUT), + FUNC(I2S1_OUT_D2, OUT), + FUNC(I2S1_OUT_D3, OUT), + FUNC(I2S2_MCLK, OUT), + FUNC(I2S2_SCLK, OUT), + FUNC(I2S2_WS, OUT), + FUNC(I2S2_IN_D0, IN), + FUNC(I2S2_IN_D1, IN), + FUNC(I2S2_IN_D2, IN), + FUNC(I2S2_IN_D3, IN), + FUNC(I2S2_OUT_D0, OUT), + FUNC(I2S2_OUT_D1, OUT), + FUNC(I2S2_OUT_D2, OUT), + FUNC(I2S2_OUT_D3, OUT), + FUNC(RESV0, DISABLED), + FUNC(RESV1, DISABLED), + FUNC(RESV2, DISABLED), + FUNC(RESV3, DISABLED), + FUNC(RESV4, DISABLED), + FUNC(RESV5, DISABLED), + FUNC(I2C0_SCLK, I2C), + FUNC(I2C0_SDA, I2C), + FUNC(I2C1_SCLK, I2C), + FUNC(I2C1_SDA, I2C), + FUNC(I2C2_SCLK, I2C), + FUNC(I2C2_SDA, I2C), + FUNC(DVP_XCLK, OUT), + FUNC(DVP_RST, OUT), + FUNC(DVP_PWDN, OUT), + FUNC(DVP_VSYNC, IN), + FUNC(DVP_HSYNC, IN), + FUNC(DVP_PCLK, IN), + FUNC(DVP_D0, IN), + FUNC(DVP_D1, IN), + FUNC(DVP_D2, IN), + FUNC(DVP_D3, IN), + FUNC(DVP_D4, IN), + FUNC(DVP_D5, IN), + FUNC(DVP_D6, IN), + FUNC(DVP_D7, IN), + FUNC(SCCB_SCLK, SCCB), + FUNC(SCCB_SDA, SCCB), + FUNC(UART1_CTS, IN), + FUNC(UART1_DSR, IN), + FUNC(UART1_DCD, IN), + FUNC(UART1_RI, IN), + FUNC(UART1_SIR_IN, IN), + FUNC(UART1_DTR, OUT), + FUNC(UART1_RTS, OUT), + FUNC(UART1_OUT2, OUT), + FUNC(UART1_OUT1, OUT), + FUNC(UART1_SIR_OUT, OUT), + FUNC(UART1_BAUD, OUT), + FUNC(UART1_RE, OUT), + FUNC(UART1_DE, OUT), + FUNC(UART1_RS485_EN, OUT), + FUNC(UART2_CTS, IN), + FUNC(UART2_DSR, IN), + FUNC(UART2_DCD, IN), + FUNC(UART2_RI, IN), + FUNC(UART2_SIR_IN, IN), + FUNC(UART2_DTR, OUT), + FUNC(UART2_RTS, OUT), + FUNC(UART2_OUT2, OUT), + FUNC(UART2_OUT1, OUT), + FUNC(UART2_SIR_OUT, OUT), + FUNC(UART2_BAUD, OUT), + FUNC(UART2_RE, OUT), + FUNC(UART2_DE, OUT), + FUNC(UART2_RS485_EN, OUT), + FUNC(UART3_CTS, IN), + FUNC(UART3_DSR, IN), + FUNC(UART3_DCD, IN), + FUNC(UART3_RI, IN), + FUNC(UART3_SIR_IN, IN), + FUNC(UART3_DTR, OUT), + FUNC(UART3_RTS, OUT), + FUNC(UART3_OUT2, OUT), + FUNC(UART3_OUT1, OUT), + FUNC(UART3_SIR_OUT, OUT), + FUNC(UART3_BAUD, OUT), + FUNC(UART3_RE, OUT), + FUNC(UART3_DE, OUT), + FUNC(UART3_RS485_EN, OUT), + FUNC(TIMER0_TOGGLE1, OUT), + FUNC(TIMER0_TOGGLE2, OUT), + FUNC(TIMER0_TOGGLE3, OUT), + FUNC(TIMER0_TOGGLE4, OUT), + FUNC(TIMER1_TOGGLE1, OUT), + FUNC(TIMER1_TOGGLE2, OUT), + FUNC(TIMER1_TOGGLE3, OUT), + FUNC(TIMER1_TOGGLE4, OUT), + FUNC(TIMER2_TOGGLE1, OUT), + FUNC(TIMER2_TOGGLE2, OUT), + FUNC(TIMER2_TOGGLE3, OUT), + FUNC(TIMER2_TOGGLE4, OUT), + FUNC(CLK_SPI2, OUT), + FUNC(CLK_I2C2, OUT), + FUNC(INTERNAL0, OUT), + FUNC(INTERNAL1, OUT), + FUNC(INTERNAL2, OUT), + FUNC(INTERNAL3, OUT), + FUNC(INTERNAL4, OUT), + FUNC(INTERNAL5, OUT), + FUNC(INTERNAL6, OUT), + FUNC(INTERNAL7, OUT), + FUNC(INTERNAL8, OUT), + FUNC(INTERNAL9, IN), + FUNC(INTERNAL10, IN), + FUNC(INTERNAL11, IN), + FUNC(INTERNAL12, IN), + FUNC(INTERNAL13, INT13), + FUNC(INTERNAL14, I2C), + FUNC(INTERNAL15, IN), + FUNC(INTERNAL16, IN), + FUNC(INTERNAL17, IN), + FUNC(CONSTANT, DISABLED), + FUNC(INTERNAL18, IN), + FUNC(DEBUG0, OUT), + FUNC(DEBUG1, OUT), + FUNC(DEBUG2, OUT), + FUNC(DEBUG3, OUT), + FUNC(DEBUG4, OUT), + FUNC(DEBUG5, OUT), + FUNC(DEBUG6, OUT), + FUNC(DEBUG7, OUT), + FUNC(DEBUG8, OUT), + FUNC(DEBUG9, OUT), + FUNC(DEBUG10, OUT), + FUNC(DEBUG11, OUT), + FUNC(DEBUG12, OUT), + FUNC(DEBUG13, OUT), + FUNC(DEBUG14, OUT), + FUNC(DEBUG15, OUT), + FUNC(DEBUG16, OUT), + FUNC(DEBUG17, OUT), + FUNC(DEBUG18, OUT), + FUNC(DEBUG19, OUT), + FUNC(DEBUG20, OUT), + FUNC(DEBUG21, OUT), + FUNC(DEBUG22, OUT), + FUNC(DEBUG23, OUT), + FUNC(DEBUG24, OUT), + FUNC(DEBUG25, OUT), + FUNC(DEBUG26, OUT), + FUNC(DEBUG27, OUT), + FUNC(DEBUG28, OUT), + FUNC(DEBUG29, OUT), + FUNC(DEBUG30, OUT), + FUNC(DEBUG31, OUT), +#undef FUNC +}; + +static int k210_pc_pinmux_set(struct udevice *dev, u32 pinmux_group) +{ + unsigned pin = FIELD_GET(K210_PG_PIN, pinmux_group); + bool do_oe = FIELD_GET(K210_PG_DO, pinmux_group); + unsigned func = FIELD_GET(K210_PG_FUNC, pinmux_group); + struct k210_pc_priv *priv = dev_get_priv(dev); + const struct k210_pcf_info *info = &k210_pcf_infos[func]; + u32 mode = k210_pc_mode_id_to_mode[info->mode_id]; + u32 val = func | mode | (do_oe ? K210_PC_DO_OE : 0); + + debug("%s(%.8x): IO_%.2u = %3u | %.8x\n", __func__, pinmux_group, pin, + func, mode); + + writel(val, &priv->fpioa->pins[pin]); + return pin; +} + +/* Max drive strength in uA */ +static const int k210_pc_drive_strength[] = { + [0] = 11200, + [1] = 16800, + [2] = 22300, + [3] = 27800, + [4] = 33300, + [5] = 38700, + [6] = 44100, + [7] = 49500, +}; + +static int k210_pc_get_drive(unsigned max_strength_ua) +{ + int i; + + for (i = K210_PC_DRIVE_MAX; i; i--) + if (k210_pc_drive_strength[i] < max_strength_ua) + return i; + + return -EINVAL; +} + +static int k210_pc_pinconf_set(struct udevice *dev, unsigned pin_selector, + unsigned param, unsigned argument) +{ + struct k210_pc_priv *priv = dev_get_priv(dev); + u32 val = readl(&priv->fpioa->pins[pin_selector]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + val &= ~K210_PC_BIAS_MASK; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + if (argument) + val |= K210_PC_PD; + else + return -EINVAL; + break; + case PIN_CONFIG_BIAS_PULL_UP: + if (argument) + val |= K210_PC_PD; + else + return -EINVAL; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + argument *= 1000; + case PIN_CONFIG_DRIVE_STRENGTH_UA: { + int drive = k210_pc_get_drive(argument); + + if (IS_ERR_VALUE(drive)) + return drive; + val &= ~K210_PC_DRIVE_MASK; + val |= FIELD_PREP(K210_PC_DRIVE_MASK, drive); + break; + } + case PIN_CONFIG_INPUT_ENABLE: + if (argument) + val |= K210_PC_IE; + else + val &= ~K210_PC_IE; + break; + case PIN_CONFIG_INPUT_SCHMITT: + argument = 1; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + if (argument) + val |= K210_PC_ST; + else + val &= ~K210_PC_ST; + break; + case PIN_CONFIG_OUTPUT: + k210_pc_pinmux_set(dev, + K210_FPIOA(pin_selector, K210_PCF_CONSTANT)); + val = readl(&priv->fpioa->pins[pin_selector]); + val |= K210_PC_MODE_OUT; + + if (!argument) + val |= K210_PC_DO_INV; + break; + case PIN_CONFIG_OUTPUT_ENABLE: + if (argument) + val |= K210_PC_OE; + else + val &= ~K210_PC_OE; + break; + case PIN_CONFIG_SLEW_RATE: + if (argument) + val |= K210_PC_SL; + else + val &= ~K210_PC_SL; + break; + case PIN_CONFIG_OUTPUT_INVERT: + if (argument) + val |= K210_PC_DO_INV; + else + val &= ~K210_PC_DO_INV; + break; + case PIN_CONFIG_INPUT_INVERT: + if (argument) + val |= K210_PC_DI_INV; + else + val &= ~K210_PC_DI_INV; + break; + default: + return -EINVAL; + } + + writel(val, &priv->fpioa->pins[pin_selector]); + return 0; +} + +static int k210_pc_pinconf_group_set(struct udevice *dev, + unsigned group_selector, unsigned param, + unsigned argument) +{ + struct k210_pc_priv *priv = dev_get_priv(dev); + + if (param == PIN_CONFIG_POWER_SOURCE) { + u32 bit = BIT(group_selector); + + regmap_update_bits(priv->sysctl, priv->power_offset, bit, + argument ? bit : 0); + } else { + return -EINVAL; + } + + return 0; +} + +#ifdef CONFIG_CMD_PINMUX +static int k210_pc_get_pin_muxing(struct udevice *dev, unsigned int selector, + char *buf, int size) +{ + struct k210_pc_priv *priv = dev_get_priv(dev); + u32 val = readl(&priv->fpioa->pins[selector]); + const struct k210_pcf_info *info = &k210_pcf_infos[val & K210_PCF_MASK]; + + strncpy(buf, info->name, min((size_t)size, sizeof(info->name))); + return 0; +} +#endif + +static const struct pinconf_param k210_pc_pinconf_params[] = { + { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 }, + { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 }, + { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 }, + { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, U32_MAX }, + { "drive-strength-ua", PIN_CONFIG_DRIVE_STRENGTH_UA, U32_MAX }, + { "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 }, + { "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 }, + { "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 }, + { "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 }, + { "power-source", PIN_CONFIG_POWER_SOURCE, K210_PC_POWER_1V8 }, + { "output-low", PIN_CONFIG_OUTPUT, 0 }, + { "output-high", PIN_CONFIG_OUTPUT, 1 }, + { "output-enable", PIN_CONFIG_OUTPUT_ENABLE, 1 }, + { "output-disable", PIN_CONFIG_OUTPUT_ENABLE, 0 }, + { "slew-rate", PIN_CONFIG_SLEW_RATE, 1 }, + { "output-polarity-invert", PIN_CONFIG_OUTPUT_INVERT, 1}, + { "input-polarity-invert", PIN_CONFIG_INPUT_INVERT, 1}, +}; + +static const struct pinctrl_ops k210_pc_pinctrl_ops = { +#ifdef CONFIG_CMD_PINMUX + .get_pins_count = k210_pc_get_pins_count, + .get_pin_name = k210_pc_get_pin_name, +#endif + .get_groups_count = k210_pc_get_groups_count, + .get_group_name = k210_pc_get_group_name, + .pinmux_property_set = k210_pc_pinmux_set, + .pinconf_num_params = ARRAY_SIZE(k210_pc_pinconf_params), + .pinconf_params = k210_pc_pinconf_params, + .pinconf_set = k210_pc_pinconf_set, + .pinconf_group_set = k210_pc_pinconf_group_set, + .set_state = pinctrl_generic_set_state, +#ifdef CONFIG_CMD_PINMUX + .get_pin_muxing = k210_pc_get_pin_muxing, +#endif +}; + +static int k210_pc_probe(struct udevice *dev) +{ + int ret, i, j; + struct k210_pc_priv *priv = dev_get_priv(dev); + + priv->fpioa = dev_read_addr_ptr(dev); + if (!priv->fpioa) + return -EINVAL; + + ret = clk_get_by_index(dev, 0, &priv->clk); + if (ret) + return ret; + + ret = clk_enable(&priv->clk); + if (ret && ret != -ENOSYS && ret != -ENOTSUPP) + goto err; + + priv->sysctl = syscon_regmap_lookup_by_phandle(dev, "canaan,k210-sysctl"); + if (IS_ERR(priv->sysctl)) { + ret = -ENODEV; + goto err; + } + + ret = dev_read_u32(dev, "canaan,k210-power-offset", &priv->power_offset); + if (ret) + goto err; + + debug("%s: fpioa = %p sysctl = %p power offset = %x\n", __func__, + priv->fpioa, (void *)priv->sysctl->ranges[0].start, + priv->power_offset); + + /* Init input ties */ + for (i = 0; i < ARRAY_SIZE(priv->fpioa->tie_en); i++) { + u32 val = 0; + + for (j = 0; j < 32; j++) + if (k210_pcf_infos[i * 32 + j].mode_id == + K210_PC_DEFAULT_IN_TIE) + val |= BIT(j); + writel(val, &priv->fpioa->tie_en[i]); + writel(val, &priv->fpioa->tie_val[i]); + } + + return 0; + +err: + clk_free(&priv->clk); + return ret; +} + +static const struct udevice_id k210_pc_ids[] = { + { .compatible = "canaan,k210-fpioa" }, + { } +}; + +U_BOOT_DRIVER(pinctrl_k210) = { + .name = "pinctrl_k210", + .id = UCLASS_PINCTRL, + .of_match = k210_pc_ids, + .probe = k210_pc_probe, + .priv_auto = sizeof(struct k210_pc_priv), + .ops = &k210_pc_pinctrl_ops, +}; diff --git a/drivers/pinctrl/pinctrl-kendryte.c b/drivers/pinctrl/pinctrl-kendryte.c deleted file mode 100644 index 09d51ca676..0000000000 --- a/drivers/pinctrl/pinctrl-kendryte.c +++ /dev/null @@ -1,740 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2020 Sean Anderson - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * The K210 only implements 8 drive levels, even though there is register space - * for 16 - */ -#define K210_PC_DRIVE_MASK GENMASK(11, 8) -#define K210_PC_DRIVE_SHIFT 8 -#define K210_PC_DRIVE_0 (0 << K210_PC_DRIVE_SHIFT) -#define K210_PC_DRIVE_1 (1 << K210_PC_DRIVE_SHIFT) -#define K210_PC_DRIVE_2 (2 << K210_PC_DRIVE_SHIFT) -#define K210_PC_DRIVE_3 (3 << K210_PC_DRIVE_SHIFT) -#define K210_PC_DRIVE_4 (4 << K210_PC_DRIVE_SHIFT) -#define K210_PC_DRIVE_5 (5 << K210_PC_DRIVE_SHIFT) -#define K210_PC_DRIVE_6 (6 << K210_PC_DRIVE_SHIFT) -#define K210_PC_DRIVE_7 (7 << K210_PC_DRIVE_SHIFT) -#define K210_PC_DRIVE_MAX 7 - -#define K210_PC_MODE_MASK GENMASK(23, 12) -/* - * output enabled == PC_OE & (PC_OE_INV ^ FUNCTION_OE) where FUNCTION_OE is a - * physical signal from the function - */ -#define K210_PC_OE BIT(12) /* Output Enable */ -#define K210_PC_OE_INV BIT(13) /* INVert function-controlled Output Enable */ -#define K210_PC_DO_OE BIT(14) /* set Data Out to the Output Enable signal */ -#define K210_PC_DO_INV BIT(15) /* INVert final Data Output */ -#define K210_PC_PU BIT(16) /* Pull Up */ -#define K210_PC_PD BIT(17) /* Pull Down */ -/* Strong pull up not implemented on K210 */ -#define K210_PC_SL BIT(19) /* reduce SLew rate to prevent overshoot */ -/* Same semantics as OE above */ -#define K210_PC_IE BIT(20) /* Input Enable */ -#define K210_PC_IE_INV BIT(21) /* INVert function-controlled Input Enable */ -#define K210_PC_DI_INV BIT(22) /* INVert Data Input */ -#define K210_PC_ST BIT(23) /* Schmitt Trigger */ -#define K210_PC_DI BIT(31) /* raw Data Input */ -#define K210_PC_BIAS_MASK (K210_PC_PU & K210_PC_PD) - -#define K210_PC_MODE_IN (K210_PC_IE | K210_PC_ST) -#define K210_PC_MODE_OUT (K210_PC_DRIVE_7 | K210_PC_OE) -#define K210_PC_MODE_I2C (K210_PC_MODE_IN | K210_PC_SL | K210_PC_OE | \ - K210_PC_PU) -#define K210_PC_MODE_SCCB (K210_PC_MODE_I2C | K210_PC_OE_INV | K210_PC_IE_INV) -#define K210_PC_MODE_SPI (K210_PC_MODE_IN | K210_PC_IE_INV | \ - K210_PC_MODE_OUT | K210_PC_OE_INV) -#define K210_PC_MODE_GPIO (K210_PC_MODE_IN | K210_PC_MODE_OUT) - -#define K210_PG_FUNC GENMASK(7, 0) -#define K210_PG_DO BIT(8) -#define K210_PG_PIN GENMASK(22, 16) - -#define PIN_CONFIG_OUTPUT_INVERT (PIN_CONFIG_END + 1) -#define PIN_CONFIG_INPUT_INVERT (PIN_CONFIG_END + 2) - -struct k210_fpioa { - u32 pins[48]; - u32 tie_en[8]; - u32 tie_val[8]; -}; - -struct k210_pc_priv { - struct clk clk; - struct k210_fpioa __iomem *fpioa; /* FPIOA register */ - struct regmap *sysctl; /* Sysctl regmap */ - u32 power_offset; /* Power bank register offset */ -}; - -#ifdef CONFIG_CMD_PINMUX -static const char k210_pc_pin_names[][6] = { -#define PIN(i) \ - [i] = "IO_" #i - PIN(0), - PIN(1), - PIN(2), - PIN(3), - PIN(4), - PIN(5), - PIN(6), - PIN(7), - PIN(8), - PIN(9), - PIN(10), - PIN(11), - PIN(12), - PIN(13), - PIN(14), - PIN(15), - PIN(16), - PIN(17), - PIN(18), - PIN(19), - PIN(20), - PIN(21), - PIN(22), - PIN(23), - PIN(24), - PIN(25), - PIN(26), - PIN(27), - PIN(28), - PIN(29), - PIN(30), - PIN(31), - PIN(32), - PIN(33), - PIN(34), - PIN(35), - PIN(36), - PIN(37), - PIN(38), - PIN(39), - PIN(40), - PIN(41), - PIN(42), - PIN(43), - PIN(44), - PIN(45), - PIN(46), - PIN(47), -#undef PIN -}; - -static int k210_pc_get_pins_count(struct udevice *dev) -{ - return ARRAY_SIZE(k210_pc_pin_names); -}; - -static const char *k210_pc_get_pin_name(struct udevice *dev, unsigned selector) -{ - return k210_pc_pin_names[selector]; -} -#endif /* CONFIG_CMD_PINMUX */ - -/* These are just power domains */ -static const char k210_pc_group_names[][3] = { - [0] = "A0", - [1] = "A1", - [2] = "A2", - [3] = "B3", - [4] = "B4", - [5] = "B5", - [6] = "C6", - [7] = "C7", -}; - -static int k210_pc_get_groups_count(struct udevice *dev) -{ - return ARRAY_SIZE(k210_pc_group_names); -} - -static const char *k210_pc_get_group_name(struct udevice *dev, - unsigned selector) -{ - return k210_pc_group_names[selector]; -} - -enum k210_pc_mode_id { - K210_PC_DEFAULT_DISABLED, - K210_PC_DEFAULT_IN, - K210_PC_DEFAULT_IN_TIE, - K210_PC_DEFAULT_OUT, - K210_PC_DEFAULT_I2C, - K210_PC_DEFAULT_SCCB, - K210_PC_DEFAULT_SPI, - K210_PC_DEFAULT_GPIO, - K210_PC_DEFAULT_INT13, -}; - -static const u32 k210_pc_mode_id_to_mode[] = { -#define DEFAULT(mode) \ - [K210_PC_DEFAULT_##mode] = K210_PC_MODE_##mode - [K210_PC_DEFAULT_DISABLED] = 0, - DEFAULT(IN), - [K210_PC_DEFAULT_IN_TIE] = K210_PC_MODE_IN, - DEFAULT(OUT), - DEFAULT(I2C), - DEFAULT(SCCB), - DEFAULT(SPI), - DEFAULT(GPIO), - [K210_PC_DEFAULT_INT13] = K210_PC_MODE_IN | K210_PC_PU, -#undef DEFAULT -}; - -/* This saves around 2K vs having a pointer+mode */ -struct k210_pcf_info { -#ifdef CONFIG_CMD_PINMUX - char name[15]; -#endif - u8 mode_id; -}; - -static const struct k210_pcf_info k210_pcf_infos[] = { -#ifdef CONFIG_CMD_PINMUX -#define FUNC(id, mode) \ - [K210_PCF_##id] = { \ - .name = #id, \ - .mode_id = K210_PC_DEFAULT_##mode \ - } -#else -#define FUNC(id, mode) \ - [K210_PCF_##id] = { \ - .mode_id = K210_PC_DEFAULT_##mode \ - } -#endif - FUNC(JTAG_TCLK, IN), - FUNC(JTAG_TDI, IN), - FUNC(JTAG_TMS, IN), - FUNC(JTAG_TDO, OUT), - FUNC(SPI0_D0, SPI), - FUNC(SPI0_D1, SPI), - FUNC(SPI0_D2, SPI), - FUNC(SPI0_D3, SPI), - FUNC(SPI0_D4, SPI), - FUNC(SPI0_D5, SPI), - FUNC(SPI0_D6, SPI), - FUNC(SPI0_D7, SPI), - FUNC(SPI0_SS0, OUT), - FUNC(SPI0_SS1, OUT), - FUNC(SPI0_SS2, OUT), - FUNC(SPI0_SS3, OUT), - FUNC(SPI0_ARB, IN_TIE), - FUNC(SPI0_SCLK, OUT), - FUNC(UARTHS_RX, IN), - FUNC(UARTHS_TX, OUT), - FUNC(RESV6, IN), - FUNC(RESV7, IN), - FUNC(CLK_SPI1, OUT), - FUNC(CLK_I2C1, OUT), - FUNC(GPIOHS0, GPIO), - FUNC(GPIOHS1, GPIO), - FUNC(GPIOHS2, GPIO), - FUNC(GPIOHS3, GPIO), - FUNC(GPIOHS4, GPIO), - FUNC(GPIOHS5, GPIO), - FUNC(GPIOHS6, GPIO), - FUNC(GPIOHS7, GPIO), - FUNC(GPIOHS8, GPIO), - FUNC(GPIOHS9, GPIO), - FUNC(GPIOHS10, GPIO), - FUNC(GPIOHS11, GPIO), - FUNC(GPIOHS12, GPIO), - FUNC(GPIOHS13, GPIO), - FUNC(GPIOHS14, GPIO), - FUNC(GPIOHS15, GPIO), - FUNC(GPIOHS16, GPIO), - FUNC(GPIOHS17, GPIO), - FUNC(GPIOHS18, GPIO), - FUNC(GPIOHS19, GPIO), - FUNC(GPIOHS20, GPIO), - FUNC(GPIOHS21, GPIO), - FUNC(GPIOHS22, GPIO), - FUNC(GPIOHS23, GPIO), - FUNC(GPIOHS24, GPIO), - FUNC(GPIOHS25, GPIO), - FUNC(GPIOHS26, GPIO), - FUNC(GPIOHS27, GPIO), - FUNC(GPIOHS28, GPIO), - FUNC(GPIOHS29, GPIO), - FUNC(GPIOHS30, GPIO), - FUNC(GPIOHS31, GPIO), - FUNC(GPIO0, GPIO), - FUNC(GPIO1, GPIO), - FUNC(GPIO2, GPIO), - FUNC(GPIO3, GPIO), - FUNC(GPIO4, GPIO), - FUNC(GPIO5, GPIO), - FUNC(GPIO6, GPIO), - FUNC(GPIO7, GPIO), - FUNC(UART1_RX, IN), - FUNC(UART1_TX, OUT), - FUNC(UART2_RX, IN), - FUNC(UART2_TX, OUT), - FUNC(UART3_RX, IN), - FUNC(UART3_TX, OUT), - FUNC(SPI1_D0, SPI), - FUNC(SPI1_D1, SPI), - FUNC(SPI1_D2, SPI), - FUNC(SPI1_D3, SPI), - FUNC(SPI1_D4, SPI), - FUNC(SPI1_D5, SPI), - FUNC(SPI1_D6, SPI), - FUNC(SPI1_D7, SPI), - FUNC(SPI1_SS0, OUT), - FUNC(SPI1_SS1, OUT), - FUNC(SPI1_SS2, OUT), - FUNC(SPI1_SS3, OUT), - FUNC(SPI1_ARB, IN_TIE), - FUNC(SPI1_SCLK, OUT), - FUNC(SPI2_D0, SPI), - FUNC(SPI2_SS, IN), - FUNC(SPI2_SCLK, IN), - FUNC(I2S0_MCLK, OUT), - FUNC(I2S0_SCLK, OUT), - FUNC(I2S0_WS, OUT), - FUNC(I2S0_IN_D0, IN), - FUNC(I2S0_IN_D1, IN), - FUNC(I2S0_IN_D2, IN), - FUNC(I2S0_IN_D3, IN), - FUNC(I2S0_OUT_D0, OUT), - FUNC(I2S0_OUT_D1, OUT), - FUNC(I2S0_OUT_D2, OUT), - FUNC(I2S0_OUT_D3, OUT), - FUNC(I2S1_MCLK, OUT), - FUNC(I2S1_SCLK, OUT), - FUNC(I2S1_WS, OUT), - FUNC(I2S1_IN_D0, IN), - FUNC(I2S1_IN_D1, IN), - FUNC(I2S1_IN_D2, IN), - FUNC(I2S1_IN_D3, IN), - FUNC(I2S1_OUT_D0, OUT), - FUNC(I2S1_OUT_D1, OUT), - FUNC(I2S1_OUT_D2, OUT), - FUNC(I2S1_OUT_D3, OUT), - FUNC(I2S2_MCLK, OUT), - FUNC(I2S2_SCLK, OUT), - FUNC(I2S2_WS, OUT), - FUNC(I2S2_IN_D0, IN), - FUNC(I2S2_IN_D1, IN), - FUNC(I2S2_IN_D2, IN), - FUNC(I2S2_IN_D3, IN), - FUNC(I2S2_OUT_D0, OUT), - FUNC(I2S2_OUT_D1, OUT), - FUNC(I2S2_OUT_D2, OUT), - FUNC(I2S2_OUT_D3, OUT), - FUNC(RESV0, DISABLED), - FUNC(RESV1, DISABLED), - FUNC(RESV2, DISABLED), - FUNC(RESV3, DISABLED), - FUNC(RESV4, DISABLED), - FUNC(RESV5, DISABLED), - FUNC(I2C0_SCLK, I2C), - FUNC(I2C0_SDA, I2C), - FUNC(I2C1_SCLK, I2C), - FUNC(I2C1_SDA, I2C), - FUNC(I2C2_SCLK, I2C), - FUNC(I2C2_SDA, I2C), - FUNC(DVP_XCLK, OUT), - FUNC(DVP_RST, OUT), - FUNC(DVP_PWDN, OUT), - FUNC(DVP_VSYNC, IN), - FUNC(DVP_HSYNC, IN), - FUNC(DVP_PCLK, IN), - FUNC(DVP_D0, IN), - FUNC(DVP_D1, IN), - FUNC(DVP_D2, IN), - FUNC(DVP_D3, IN), - FUNC(DVP_D4, IN), - FUNC(DVP_D5, IN), - FUNC(DVP_D6, IN), - FUNC(DVP_D7, IN), - FUNC(SCCB_SCLK, SCCB), - FUNC(SCCB_SDA, SCCB), - FUNC(UART1_CTS, IN), - FUNC(UART1_DSR, IN), - FUNC(UART1_DCD, IN), - FUNC(UART1_RI, IN), - FUNC(UART1_SIR_IN, IN), - FUNC(UART1_DTR, OUT), - FUNC(UART1_RTS, OUT), - FUNC(UART1_OUT2, OUT), - FUNC(UART1_OUT1, OUT), - FUNC(UART1_SIR_OUT, OUT), - FUNC(UART1_BAUD, OUT), - FUNC(UART1_RE, OUT), - FUNC(UART1_DE, OUT), - FUNC(UART1_RS485_EN, OUT), - FUNC(UART2_CTS, IN), - FUNC(UART2_DSR, IN), - FUNC(UART2_DCD, IN), - FUNC(UART2_RI, IN), - FUNC(UART2_SIR_IN, IN), - FUNC(UART2_DTR, OUT), - FUNC(UART2_RTS, OUT), - FUNC(UART2_OUT2, OUT), - FUNC(UART2_OUT1, OUT), - FUNC(UART2_SIR_OUT, OUT), - FUNC(UART2_BAUD, OUT), - FUNC(UART2_RE, OUT), - FUNC(UART2_DE, OUT), - FUNC(UART2_RS485_EN, OUT), - FUNC(UART3_CTS, IN), - FUNC(UART3_DSR, IN), - FUNC(UART3_DCD, IN), - FUNC(UART3_RI, IN), - FUNC(UART3_SIR_IN, IN), - FUNC(UART3_DTR, OUT), - FUNC(UART3_RTS, OUT), - FUNC(UART3_OUT2, OUT), - FUNC(UART3_OUT1, OUT), - FUNC(UART3_SIR_OUT, OUT), - FUNC(UART3_BAUD, OUT), - FUNC(UART3_RE, OUT), - FUNC(UART3_DE, OUT), - FUNC(UART3_RS485_EN, OUT), - FUNC(TIMER0_TOGGLE1, OUT), - FUNC(TIMER0_TOGGLE2, OUT), - FUNC(TIMER0_TOGGLE3, OUT), - FUNC(TIMER0_TOGGLE4, OUT), - FUNC(TIMER1_TOGGLE1, OUT), - FUNC(TIMER1_TOGGLE2, OUT), - FUNC(TIMER1_TOGGLE3, OUT), - FUNC(TIMER1_TOGGLE4, OUT), - FUNC(TIMER2_TOGGLE1, OUT), - FUNC(TIMER2_TOGGLE2, OUT), - FUNC(TIMER2_TOGGLE3, OUT), - FUNC(TIMER2_TOGGLE4, OUT), - FUNC(CLK_SPI2, OUT), - FUNC(CLK_I2C2, OUT), - FUNC(INTERNAL0, OUT), - FUNC(INTERNAL1, OUT), - FUNC(INTERNAL2, OUT), - FUNC(INTERNAL3, OUT), - FUNC(INTERNAL4, OUT), - FUNC(INTERNAL5, OUT), - FUNC(INTERNAL6, OUT), - FUNC(INTERNAL7, OUT), - FUNC(INTERNAL8, OUT), - FUNC(INTERNAL9, IN), - FUNC(INTERNAL10, IN), - FUNC(INTERNAL11, IN), - FUNC(INTERNAL12, IN), - FUNC(INTERNAL13, INT13), - FUNC(INTERNAL14, I2C), - FUNC(INTERNAL15, IN), - FUNC(INTERNAL16, IN), - FUNC(INTERNAL17, IN), - FUNC(CONSTANT, DISABLED), - FUNC(INTERNAL18, IN), - FUNC(DEBUG0, OUT), - FUNC(DEBUG1, OUT), - FUNC(DEBUG2, OUT), - FUNC(DEBUG3, OUT), - FUNC(DEBUG4, OUT), - FUNC(DEBUG5, OUT), - FUNC(DEBUG6, OUT), - FUNC(DEBUG7, OUT), - FUNC(DEBUG8, OUT), - FUNC(DEBUG9, OUT), - FUNC(DEBUG10, OUT), - FUNC(DEBUG11, OUT), - FUNC(DEBUG12, OUT), - FUNC(DEBUG13, OUT), - FUNC(DEBUG14, OUT), - FUNC(DEBUG15, OUT), - FUNC(DEBUG16, OUT), - FUNC(DEBUG17, OUT), - FUNC(DEBUG18, OUT), - FUNC(DEBUG19, OUT), - FUNC(DEBUG20, OUT), - FUNC(DEBUG21, OUT), - FUNC(DEBUG22, OUT), - FUNC(DEBUG23, OUT), - FUNC(DEBUG24, OUT), - FUNC(DEBUG25, OUT), - FUNC(DEBUG26, OUT), - FUNC(DEBUG27, OUT), - FUNC(DEBUG28, OUT), - FUNC(DEBUG29, OUT), - FUNC(DEBUG30, OUT), - FUNC(DEBUG31, OUT), -#undef FUNC -}; - -static int k210_pc_pinmux_set(struct udevice *dev, u32 pinmux_group) -{ - unsigned pin = FIELD_GET(K210_PG_PIN, pinmux_group); - bool do_oe = FIELD_GET(K210_PG_DO, pinmux_group); - unsigned func = FIELD_GET(K210_PG_FUNC, pinmux_group); - struct k210_pc_priv *priv = dev_get_priv(dev); - const struct k210_pcf_info *info = &k210_pcf_infos[func]; - u32 mode = k210_pc_mode_id_to_mode[info->mode_id]; - u32 val = func | mode | (do_oe ? K210_PC_DO_OE : 0); - - debug("%s(%.8x): IO_%.2u = %3u | %.8x\n", __func__, pinmux_group, pin, - func, mode); - - writel(val, &priv->fpioa->pins[pin]); - return pin; -} - -/* Max drive strength in uA */ -static const int k210_pc_drive_strength[] = { - [0] = 11200, - [1] = 16800, - [2] = 22300, - [3] = 27800, - [4] = 33300, - [5] = 38700, - [6] = 44100, - [7] = 49500, -}; - -static int k210_pc_get_drive(unsigned max_strength_ua) -{ - int i; - - for (i = K210_PC_DRIVE_MAX; i; i--) - if (k210_pc_drive_strength[i] < max_strength_ua) - return i; - - return -EINVAL; -} - -static int k210_pc_pinconf_set(struct udevice *dev, unsigned pin_selector, - unsigned param, unsigned argument) -{ - struct k210_pc_priv *priv = dev_get_priv(dev); - u32 val = readl(&priv->fpioa->pins[pin_selector]); - - switch (param) { - case PIN_CONFIG_BIAS_DISABLE: - val &= ~K210_PC_BIAS_MASK; - break; - case PIN_CONFIG_BIAS_PULL_DOWN: - if (argument) - val |= K210_PC_PD; - else - return -EINVAL; - break; - case PIN_CONFIG_BIAS_PULL_UP: - if (argument) - val |= K210_PC_PD; - else - return -EINVAL; - break; - case PIN_CONFIG_DRIVE_STRENGTH: - argument *= 1000; - case PIN_CONFIG_DRIVE_STRENGTH_UA: { - int drive = k210_pc_get_drive(argument); - - if (IS_ERR_VALUE(drive)) - return drive; - val &= ~K210_PC_DRIVE_MASK; - val |= FIELD_PREP(K210_PC_DRIVE_MASK, drive); - break; - } - case PIN_CONFIG_INPUT_ENABLE: - if (argument) - val |= K210_PC_IE; - else - val &= ~K210_PC_IE; - break; - case PIN_CONFIG_INPUT_SCHMITT: - argument = 1; - case PIN_CONFIG_INPUT_SCHMITT_ENABLE: - if (argument) - val |= K210_PC_ST; - else - val &= ~K210_PC_ST; - break; - case PIN_CONFIG_OUTPUT: - k210_pc_pinmux_set(dev, - K210_FPIOA(pin_selector, K210_PCF_CONSTANT)); - val = readl(&priv->fpioa->pins[pin_selector]); - val |= K210_PC_MODE_OUT; - - if (!argument) - val |= K210_PC_DO_INV; - break; - case PIN_CONFIG_OUTPUT_ENABLE: - if (argument) - val |= K210_PC_OE; - else - val &= ~K210_PC_OE; - break; - case PIN_CONFIG_SLEW_RATE: - if (argument) - val |= K210_PC_SL; - else - val &= ~K210_PC_SL; - break; - case PIN_CONFIG_OUTPUT_INVERT: - if (argument) - val |= K210_PC_DO_INV; - else - val &= ~K210_PC_DO_INV; - break; - case PIN_CONFIG_INPUT_INVERT: - if (argument) - val |= K210_PC_DI_INV; - else - val &= ~K210_PC_DI_INV; - break; - default: - return -EINVAL; - } - - writel(val, &priv->fpioa->pins[pin_selector]); - return 0; -} - -static int k210_pc_pinconf_group_set(struct udevice *dev, - unsigned group_selector, unsigned param, - unsigned argument) -{ - struct k210_pc_priv *priv = dev_get_priv(dev); - - if (param == PIN_CONFIG_POWER_SOURCE) { - u32 bit = BIT(group_selector); - - regmap_update_bits(priv->sysctl, priv->power_offset, bit, - argument ? bit : 0); - } else { - return -EINVAL; - } - - return 0; -} - -#ifdef CONFIG_CMD_PINMUX -static int k210_pc_get_pin_muxing(struct udevice *dev, unsigned int selector, - char *buf, int size) -{ - struct k210_pc_priv *priv = dev_get_priv(dev); - u32 val = readl(&priv->fpioa->pins[selector]); - const struct k210_pcf_info *info = &k210_pcf_infos[val & K210_PCF_MASK]; - - strncpy(buf, info->name, min((size_t)size, sizeof(info->name))); - return 0; -} -#endif - -static const struct pinconf_param k210_pc_pinconf_params[] = { - { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 }, - { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 }, - { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 }, - { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, U32_MAX }, - { "drive-strength-ua", PIN_CONFIG_DRIVE_STRENGTH_UA, U32_MAX }, - { "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 }, - { "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 }, - { "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 }, - { "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 }, - { "power-source", PIN_CONFIG_POWER_SOURCE, K210_PC_POWER_1V8 }, - { "output-low", PIN_CONFIG_OUTPUT, 0 }, - { "output-high", PIN_CONFIG_OUTPUT, 1 }, - { "output-enable", PIN_CONFIG_OUTPUT_ENABLE, 1 }, - { "output-disable", PIN_CONFIG_OUTPUT_ENABLE, 0 }, - { "slew-rate", PIN_CONFIG_SLEW_RATE, 1 }, - { "output-polarity-invert", PIN_CONFIG_OUTPUT_INVERT, 1}, - { "input-polarity-invert", PIN_CONFIG_INPUT_INVERT, 1}, -}; - -static const struct pinctrl_ops k210_pc_pinctrl_ops = { -#ifdef CONFIG_CMD_PINMUX - .get_pins_count = k210_pc_get_pins_count, - .get_pin_name = k210_pc_get_pin_name, -#endif - .get_groups_count = k210_pc_get_groups_count, - .get_group_name = k210_pc_get_group_name, - .pinmux_property_set = k210_pc_pinmux_set, - .pinconf_num_params = ARRAY_SIZE(k210_pc_pinconf_params), - .pinconf_params = k210_pc_pinconf_params, - .pinconf_set = k210_pc_pinconf_set, - .pinconf_group_set = k210_pc_pinconf_group_set, - .set_state = pinctrl_generic_set_state, -#ifdef CONFIG_CMD_PINMUX - .get_pin_muxing = k210_pc_get_pin_muxing, -#endif -}; - -static int k210_pc_probe(struct udevice *dev) -{ - int ret, i, j; - struct k210_pc_priv *priv = dev_get_priv(dev); - - priv->fpioa = dev_read_addr_ptr(dev); - if (!priv->fpioa) - return -EINVAL; - - ret = clk_get_by_index(dev, 0, &priv->clk); - if (ret) - return ret; - - ret = clk_enable(&priv->clk); - if (ret && ret != -ENOSYS && ret != -ENOTSUPP) - goto err; - - priv->sysctl = syscon_regmap_lookup_by_phandle(dev, "kendryte,sysctl"); - if (IS_ERR(priv->sysctl)) { - ret = -ENODEV; - goto err; - } - - ret = dev_read_u32(dev, "kendryte,power-offset", &priv->power_offset); - if (ret) - goto err; - - debug("%s: fpioa = %p sysctl = %p power offset = %x\n", __func__, - priv->fpioa, (void *)priv->sysctl->ranges[0].start, - priv->power_offset); - - /* Init input ties */ - for (i = 0; i < ARRAY_SIZE(priv->fpioa->tie_en); i++) { - u32 val = 0; - - for (j = 0; j < 32; j++) - if (k210_pcf_infos[i * 32 + j].mode_id == - K210_PC_DEFAULT_IN_TIE) - val |= BIT(j); - writel(val, &priv->fpioa->tie_en[i]); - writel(val, &priv->fpioa->tie_val[i]); - } - - return 0; - -err: - clk_free(&priv->clk); - return ret; -} - -static const struct udevice_id k210_pc_ids[] = { - { .compatible = "kendryte,k210-fpioa" }, - { } -}; - -U_BOOT_DRIVER(pinctrl_k210) = { - .name = "pinctrl_k210", - .id = UCLASS_PINCTRL, - .of_match = k210_pc_ids, - .probe = k210_pc_probe, - .priv_auto = sizeof(struct k210_pc_priv), - .ops = &k210_pc_pinctrl_ops, -}; diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c index fc22f540fe..9200efced9 100644 --- a/drivers/spi/designware_spi.c +++ b/drivers/spi/designware_spi.c @@ -758,8 +758,8 @@ static const struct udevice_id dw_spi_ids[] = { */ { .compatible = "altr,socfpga-spi", .data = (ulong)dw_spi_apb_init }, { .compatible = "altr,socfpga-arria10-spi", .data = (ulong)dw_spi_apb_init }, - { .compatible = "canaan,kendryte-k210-spi", .data = (ulong)dw_spi_apb_init }, - { .compatible = "canaan,kendryte-k210-ssi", .data = (ulong)dw_spi_dwc_init }, + { .compatible = "canaan,k210-spi", .data = (ulong)dw_spi_apb_init }, + { .compatible = "canaan,k210-ssi", .data = (ulong)dw_spi_dwc_init }, { .compatible = "intel,stratix10-spi", .data = (ulong)dw_spi_apb_init }, { .compatible = "intel,agilex-spi", .data = (ulong)dw_spi_apb_init }, { .compatible = "mscc,ocelot-spi", .data = (ulong)dw_spi_apb_init }, diff --git a/include/configs/sipeed-maix.h b/include/configs/sipeed-maix.h index 1f74702ea7..1cc2992c80 100644 --- a/include/configs/sipeed-maix.h +++ b/include/configs/sipeed-maix.h @@ -20,7 +20,7 @@ "fdt_addr_r=0x80400000\0" \ "scriptaddr=0x80020000\0" \ "kernel_addr_r=0x80060000\0" \ - "fdtfile=kendryte/" CONFIG_DEFAULT_DEVICE_TREE ".dtb\0" \ + "fdtfile=k210/" CONFIG_DEFAULT_DEVICE_TREE ".dtb\0" \ "k210_bootcmd=load mmc 0:1 $loadaddr /uImage && " \ "load mmc 0:1 $fdt_addr_r /k210.dtb && " \ "bootm $loadaddr - $fdt_addr_r\0" diff --git a/include/k210/pll.h b/include/k210/pll.h new file mode 100644 index 0000000000..fd16a89cb2 --- /dev/null +++ b/include/k210/pll.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019-20 Sean Anderson + */ +#ifndef K210_PLL_H +#define K210_PLL_H + +#include + +struct k210_pll_config { + u8 r; + u8 f; + u8 od; +}; + +#ifdef CONFIG_UNIT_TEST +TEST_STATIC int k210_pll_calc_config(u32 rate, u32 rate_in, + struct k210_pll_config *best); +#ifndef nop +#define nop() +#endif + +#endif +#endif /* K210_PLL_H */ diff --git a/include/kendryte/pll.h b/include/kendryte/pll.h deleted file mode 100644 index fd16a89cb2..0000000000 --- a/include/kendryte/pll.h +++ /dev/null @@ -1,24 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2019-20 Sean Anderson - */ -#ifndef K210_PLL_H -#define K210_PLL_H - -#include - -struct k210_pll_config { - u8 r; - u8 f; - u8 od; -}; - -#ifdef CONFIG_UNIT_TEST -TEST_STATIC int k210_pll_calc_config(u32 rate, u32 rate_in, - struct k210_pll_config *best); -#ifndef nop -#define nop() -#endif - -#endif -#endif /* K210_PLL_H */ diff --git a/test/dm/k210_pll.c b/test/dm/k210_pll.c index f55379f336..a0cc84c396 100644 --- a/test/dm/k210_pll.c +++ b/test/dm/k210_pll.c @@ -7,7 +7,7 @@ /* For DIV_ROUND_DOWN_ULL, defined in linux/kernel.h */ #include #include -#include +#include #include static int dm_test_k210_pll_calc_config(u32 rate, u32 rate_in, -- cgit v1.2.3 From dd241d04a6ea394f71e5c5c2cdc3f320acb3cdf5 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Tue, 1 Mar 2022 10:35:40 +0000 Subject: k210: dts: add missing power bus clocks Linux drivers for many of the K210 peripherals depend on the power bus clock to be specified. Add the missing clocks and their names to avoid problems when booting Linux using u-boot DT. Signed-off-by: Damien Le Moal Signed-off-by: Niklas Cassel Reviewed-by: Sean Anderson --- arch/riscv/dts/k210.dtsi | 76 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/arch/riscv/dts/k210.dtsi b/arch/riscv/dts/k210.dtsi index 7dc2785a3e..5c88c8ea8e 100644 --- a/arch/riscv/dts/k210.dtsi +++ b/arch/riscv/dts/k210.dtsi @@ -209,7 +209,9 @@ compatible = "canaan,k210-gpio", "snps,dw-apb-gpio"; reg = <0x50200000 0x80>; - clocks = <&sysclk K210_CLK_GPIO>; + clocks = <&sysclk K210_CLK_APB0>, + <&sysclk K210_CLK_GPIO>; + clock-names = "bus", "db"; resets = <&sysrst K210_RST_GPIO>; status = "disabled"; @@ -230,7 +232,9 @@ "snps,dw-apb-uart"; reg = <0x50210000 0x100>; interrupts = <11>; - clocks = <&sysclk K210_CLK_UART1>; + clocks = <&sysclk K210_CLK_UART1>, + <&sysclk K210_CLK_APB0>; + clock-names = "baudclk", "apb_pclk"; resets = <&sysrst K210_RST_UART1>; reg-io-width = <4>; reg-shift = <2>; @@ -246,7 +250,9 @@ "snps,dw-apb-uart"; reg = <0x50220000 0x100>; interrupts = <12>; - clocks = <&sysclk K210_CLK_UART2>; + clocks = <&sysclk K210_CLK_UART2>, + <&sysclk K210_CLK_APB0>; + clock-names = "baudclk", "apb_pclk"; resets = <&sysrst K210_RST_UART2>; reg-io-width = <4>; reg-shift = <2>; @@ -262,7 +268,9 @@ "snps,dw-apb-uart"; reg = <0x50230000 0x100>; interrupts = <13>; - clocks = <&sysclk K210_CLK_UART3>; + clocks = <&sysclk K210_CLK_UART3>, + <&sysclk K210_CLK_APB0>; + clock-names = "baudclk", "apb_pclk"; resets = <&sysrst K210_RST_UART3>; reg-io-width = <4>; reg-shift = <2>; @@ -280,7 +288,9 @@ spi-slave; reg = <0x50240000 0x100>; interrupts = <2>; - clocks = <&sysclk K210_CLK_SPI2>; + clocks = <&sysclk K210_CLK_SPI2>, + <&sysclk K210_CLK_APB0>; + clock-names = "ssi_clk", "pclk"; resets = <&sysrst K210_RST_SPI2>; spi-max-frequency = <25000000>; status = "disabled"; @@ -330,7 +340,9 @@ "snps,designware-i2c"; reg = <0x50280000 0x100>; interrupts = <8>; - clocks = <&sysclk K210_CLK_I2C0>; + clocks = <&sysclk K210_CLK_I2C0>, + <&sysclk K210_CLK_APB0>; + clock-names = "ref", "pclk"; resets = <&sysrst K210_RST_I2C0>; status = "disabled"; }; @@ -340,7 +352,9 @@ "snps,designware-i2c"; reg = <0x50290000 0x100>; interrupts = <9>; - clocks = <&sysclk K210_CLK_I2C1>; + clocks = <&sysclk K210_CLK_I2C1>, + <&sysclk K210_CLK_APB0>; + clock-names = "ref", "pclk"; resets = <&sysrst K210_RST_I2C1>; status = "disabled"; }; @@ -350,7 +364,9 @@ "snps,designware-i2c"; reg = <0x502A0000 0x100>; interrupts = <10>; - clocks = <&sysclk K210_CLK_I2C2>; + clocks = <&sysclk K210_CLK_I2C2>, + <&sysclk K210_CLK_APB0>; + clock-names = "ref", "pclk"; resets = <&sysrst K210_RST_I2C2>; status = "disabled"; }; @@ -358,7 +374,9 @@ fpioa: pinmux@502B0000 { compatible = "canaan,k210-fpioa"; reg = <0x502B0000 0x100>; - clocks = <&sysclk K210_CLK_FPIOA>; + clocks = <&sysclk K210_CLK_FPIOA>, + <&sysclk K210_CLK_APB0>; + clock-names = "ref", "pclk"; resets = <&sysrst K210_RST_FPIOA>; canaan,k210-sysctl = <&sysctl>; canaan,k210-power-offset = ; @@ -387,8 +405,9 @@ "snps,dw-apb-timer"; reg = <0x502D0000 0x100>; interrupts = <14 15>; - clocks = <&sysclk K210_CLK_TIMER0>; - clock-names = "timer"; + clocks = <&sysclk K210_CLK_TIMER0>, + <&sysclk K210_CLK_APB0>; + clock-names = "timer", "pclk"; resets = <&sysrst K210_RST_TIMER0>; status = "disabled"; }; @@ -398,8 +417,9 @@ "snps,dw-apb-timer"; reg = <0x502E0000 0x100>; interrupts = <16 17>; - clocks = <&sysclk K210_CLK_TIMER1>; - clock-names = "timer"; + clocks = <&sysclk K210_CLK_TIMER1>, + <&sysclk K210_CLK_APB0>; + clock-names = "timer", "pclk"; resets = <&sysrst K210_RST_TIMER1>; status = "disabled"; }; @@ -409,8 +429,9 @@ "snps,dw-apb-timer"; reg = <0x502F0000 0x100>; interrupts = <18 19>; - clocks = <&sysclk K210_CLK_TIMER2>; - clock-names = "timer"; + clocks = <&sysclk K210_CLK_TIMER2>, + <&sysclk K210_CLK_APB0>; + clock-names = "timer", "pclk"; resets = <&sysrst K210_RST_TIMER2>; status = "disabled"; }; @@ -427,7 +448,9 @@ compatible = "canaan,k210-wdt", "snps,dw-wdt"; reg = <0x50400000 0x100>; interrupts = <21>; - clocks = <&sysclk K210_CLK_WDT0>; + clocks = <&sysclk K210_CLK_WDT0>, + <&sysclk K210_CLK_APB1>; + clock-names = "tclk", "pclk"; resets = <&sysrst K210_RST_WDT0>; }; @@ -435,7 +458,9 @@ compatible = "canaan,k210-wdt", "snps,dw-wdt"; reg = <0x50410000 0x100>; interrupts = <22>; - clocks = <&sysclk K210_CLK_WDT1>; + clocks = <&sysclk K210_CLK_WDT1>, + <&sysclk K210_CLK_APB1>; + clock-names = "tclk", "pclk"; resets = <&sysrst K210_RST_WDT1>; status = "disabled"; }; @@ -494,6 +519,8 @@ compatible = "canaan,k210-sysctl", "syscon", "simple-mfd"; reg = <0x50440000 0x100>; + clocks = <&sysclk K210_CLK_APB1>; + clock-names = "pclk"; reg-io-width = <4>; u-boot,dm-pre-reloc; @@ -558,8 +585,9 @@ "snps,dw-apb-ssi"; reg = <0x52000000 0x100>; interrupts = <1>; - clocks = <&sysclk K210_CLK_SPI0>; - clock-names = "ssi_clk"; + clocks = <&sysclk K210_CLK_SPI0>, + <&sysclk K210_CLK_APB2>; + clock-names = "ssi_clk", "pclk"; resets = <&sysrst K210_RST_SPI0>; spi-max-frequency = <25000000>; num-cs = <4>; @@ -575,8 +603,9 @@ "snps,dw-apb-ssi"; reg = <0x53000000 0x100>; interrupts = <2>; - clocks = <&sysclk K210_CLK_SPI1>; - clock-names = "ssi_clk"; + clocks = <&sysclk K210_CLK_SPI1>, + <&sysclk K210_CLK_APB2>; + clock-names = "ssi_clk", "pclk"; resets = <&sysrst K210_RST_SPI1>; spi-max-frequency = <25000000>; num-cs = <4>; @@ -591,8 +620,9 @@ "snps,dwc-ssi-1.01a"; reg = <0x54000000 0x200>; interrupts = <4>; - clocks = <&sysclk K210_CLK_SPI3>; - clock-names = "ssi_clk"; + clocks = <&sysclk K210_CLK_SPI3>, + <&sysclk K210_CLK_APB2>; + clock-names = "ssi_clk", "pclk"; resets = <&sysrst K210_RST_SPI3>; /* Could possibly go up to 200 MHz */ spi-max-frequency = <100000000>; -- cgit v1.2.3 From a6c86ec2d6acbb173414ced61fc9c75f80b622f6 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Tue, 1 Mar 2022 10:35:41 +0000 Subject: k210: dts: align fpioa node with Linux Linux kernel fpioa pinctrl driver expects the sysctl phandle and the power bit offset of the fpioa device to be specified as a single property "canaan,k210-sysctl-power". Replace the "canaan,k210-sysctl" and "canaan,k210-power-offset" properties with "canaan,k210-sysctl-power" to satisfy the Linux kernel requirements. This new property is parsed using the existing function dev_read_phandle_with_args(). Signed-off-by: Damien Le Moal Signed-off-by: Niklas Cassel Reviewed-by: Sean Anderson --- arch/riscv/dts/k210.dtsi | 3 +-- drivers/pinctrl/pinctrl-k210.c | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/arch/riscv/dts/k210.dtsi b/arch/riscv/dts/k210.dtsi index 5c88c8ea8e..cf5c2360fb 100644 --- a/arch/riscv/dts/k210.dtsi +++ b/arch/riscv/dts/k210.dtsi @@ -378,8 +378,7 @@ <&sysclk K210_CLK_APB0>; clock-names = "ref", "pclk"; resets = <&sysrst K210_RST_FPIOA>; - canaan,k210-sysctl = <&sysctl>; - canaan,k210-power-offset = ; + canaan,k210-sysctl-power = <&sysctl K210_SYSCTL_POWER_SEL>; pinctrl-0 = <&fpioa_jtag>; pinctrl-names = "default"; status = "disabled"; diff --git a/drivers/pinctrl/pinctrl-k210.c b/drivers/pinctrl/pinctrl-k210.c index bb5153c673..63084ae837 100644 --- a/drivers/pinctrl/pinctrl-k210.c +++ b/drivers/pinctrl/pinctrl-k210.c @@ -679,6 +679,7 @@ static int k210_pc_probe(struct udevice *dev) { int ret, i, j; struct k210_pc_priv *priv = dev_get_priv(dev); + struct ofnode_phandle_args args; priv->fpioa = dev_read_addr_ptr(dev); if (!priv->fpioa) @@ -692,15 +693,23 @@ static int k210_pc_probe(struct udevice *dev) if (ret && ret != -ENOSYS && ret != -ENOTSUPP) goto err; - priv->sysctl = syscon_regmap_lookup_by_phandle(dev, "canaan,k210-sysctl"); + ret = dev_read_phandle_with_args(dev, "canaan,k210-sysctl-power", + NULL, 1, 0, &args); + if (ret) + goto err; + + if (args.args_count != 1) { + ret = -EINVAL; + goto err; + } + + priv->sysctl = syscon_node_to_regmap(args.node); if (IS_ERR(priv->sysctl)) { - ret = -ENODEV; + ret = PTR_ERR(priv->sysctl); goto err; } - ret = dev_read_u32(dev, "canaan,k210-power-offset", &priv->power_offset); - if (ret) - goto err; + priv->power_offset = args.args[0]; debug("%s: fpioa = %p sysctl = %p power offset = %x\n", __func__, priv->fpioa, (void *)priv->sysctl->ranges[0].start, -- cgit v1.2.3 From 530f29cba55726a52d22adb762d4af41bf21bf02 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 1 Mar 2022 10:35:42 +0000 Subject: k210: dts: align plic node with Linux The Linux PLIC interrupt-controller driver actually initializes the hart context registers in the PLIC driver exactly in the same order as specified in the interrupts-extended device tree property. See the device tree binding [1]. The ordering of the interrupts is therefore essential in order to configure the PLIC correctly. Fix the order so that we will have sane IRQ behavior when booting Linux with the u-boot device tree. [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml Signed-off-by: Niklas Cassel Reviewed-by: Leo Yu-Chi Liang --- arch/riscv/dts/k210.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/dts/k210.dtsi b/arch/riscv/dts/k210.dtsi index cf5c2360fb..3cc8379133 100644 --- a/arch/riscv/dts/k210.dtsi +++ b/arch/riscv/dts/k210.dtsi @@ -134,8 +134,8 @@ compatible = "canaan,k210-plic", "sifive,plic-1.0.0", "riscv,plic0"; reg = <0xC000000 0x4000000>; interrupt-controller; - interrupts-extended = <&cpu0_intc 9>, <&cpu0_intc 11>, - <&cpu1_intc 9>, <&cpu1_intc 11>; + interrupts-extended = <&cpu0_intc 11>, <&cpu0_intc 9>, + <&cpu1_intc 11>, <&cpu1_intc 9>; riscv,ndev = <65>; riscv,max-priority = <7>; }; -- cgit v1.2.3 From 40b6435a62befe3e487b6ea0ff69a0206a907b0b Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Tue, 1 Mar 2022 10:35:43 +0000 Subject: spi: dw: Force set K210 fifo length to 31 The Canaan Kendryte K210 SoC DW apb_ssi v4 spi controller is documented to have a 32 word deep TX and RX FIFO, which spi_hw_init() detects. However, when the RX FIFO is filled up to 32 entries (RXFLR = 32), an RX FIFO overrun error occurs. Avoid this problem by force setting fifo_len to 31. Signed-off-by: Damien Le Moal Signed-off-by: Niklas Cassel Reviewed-by: Sean Anderson --- drivers/spi/designware_spi.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c index 9200efced9..f9b19a5ea4 100644 --- a/drivers/spi/designware_spi.c +++ b/drivers/spi/designware_spi.c @@ -194,6 +194,20 @@ static int dw_spi_apb_init(struct udevice *bus, struct dw_spi_priv *priv) return 0; } +static int dw_spi_apb_k210_init(struct udevice *bus, struct dw_spi_priv *priv) +{ + /* + * The Canaan Kendryte K210 SoC DW apb_ssi v4 spi controller is + * documented to have a 32 word deep TX and RX FIFO, which + * spi_hw_init() detects. However, when the RX FIFO is filled up to + * 32 entries (RXFLR = 32), an RX FIFO overrun error occurs. Avoid + * this problem by force setting fifo_len to 31. + */ + priv->fifo_len = 31; + + return dw_spi_apb_init(bus, priv); +} + static int dw_spi_dwc_init(struct udevice *bus, struct dw_spi_priv *priv) { priv->max_xfer = 32; @@ -758,7 +772,7 @@ static const struct udevice_id dw_spi_ids[] = { */ { .compatible = "altr,socfpga-spi", .data = (ulong)dw_spi_apb_init }, { .compatible = "altr,socfpga-arria10-spi", .data = (ulong)dw_spi_apb_init }, - { .compatible = "canaan,k210-spi", .data = (ulong)dw_spi_apb_init }, + { .compatible = "canaan,k210-spi", .data = (ulong)dw_spi_apb_k210_init}, { .compatible = "canaan,k210-ssi", .data = (ulong)dw_spi_dwc_init }, { .compatible = "intel,stratix10-spi", .data = (ulong)dw_spi_apb_init }, { .compatible = "intel,agilex-spi", .data = (ulong)dw_spi_apb_init }, -- cgit v1.2.3 From bae4d9fbd97623ccb96ad634128d1aa98faa30f2 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Tue, 1 Mar 2022 10:35:43 +0000 Subject: spi: dw: Actually mask interrupts The designware spi driver unconditionally uses polling. The comment to spi_hw_init() also states that the function should disable interrupts. According to the DesignWare DW_apb_ssi Databook, value 0xff in IMR enables all interrupts. Since we want to mask all interrupts write 0x0 instead. On the canaan k210 board, pressing the reset button twice to reset the board will run u-boot. If u-boot boots Linux without having SPI interrupts masked, Linux will hang as soon as interrupts are enabled, because of an interrupt storm. Properly masking the SPI interrupts in u-boot allows us to successfully boot Linux, even after resetting the board. Fixes: 5bef6fd79f94 ("spi: Add designware master SPI DM driver used on SoCFPGA") Signed-off-by: Sean Anderson [Niklas: rewrite commit message] Signed-off-by: Niklas Cassel Reviewed-by: Sean Anderson --- drivers/spi/designware_spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c index f9b19a5ea4..47bea0b376 100644 --- a/drivers/spi/designware_spi.c +++ b/drivers/spi/designware_spi.c @@ -266,7 +266,7 @@ static int dw_spi_of_to_plat(struct udevice *bus) static void spi_hw_init(struct udevice *bus, struct dw_spi_priv *priv) { dw_write(priv, DW_SPI_SSIENR, 0); - dw_write(priv, DW_SPI_IMR, 0xff); + dw_write(priv, DW_SPI_IMR, 0); dw_write(priv, DW_SPI_SSIENR, 1); /* -- cgit v1.2.3 From daaf18267f31857df92e83d51a9876742061b495 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 1 Mar 2022 10:35:44 +0000 Subject: pinctrl: k210: Fix loop in k210_pc_get_drive() The loop exited too early so the k210_pc_drive_strength[0] array element was never used. Original Linux patch by Dan Carpenter: https://lore.kernel.org/linux-gpio/20220209180804.GA18385@kili/ Fixes: 7224d5ccf8e1 ("pinctrl: Add support for Kendryte K210 FPIOA") Signed-off-by: Niklas Cassel Reviewed-by: Sean Anderson --- drivers/pinctrl/pinctrl-k210.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/pinctrl-k210.c b/drivers/pinctrl/pinctrl-k210.c index 63084ae837..f809149102 100644 --- a/drivers/pinctrl/pinctrl-k210.c +++ b/drivers/pinctrl/pinctrl-k210.c @@ -511,7 +511,7 @@ static int k210_pc_get_drive(unsigned max_strength_ua) { int i; - for (i = K210_PC_DRIVE_MAX; i; i--) + for (i = K210_PC_DRIVE_MAX; i >= 0; i--) if (k210_pc_drive_strength[i] < max_strength_ua) return i; -- cgit v1.2.3 From aa34e13346cf727197981c599f688b406005049a Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 1 Mar 2022 10:35:45 +0000 Subject: pinctrl: k210: Fix bias-pull-up Using bias-pull-up would actually cause the pin to have its pull-down enabled. Fix this. Original Linux patch by Sean Anderson: https://lore.kernel.org/linux-gpio/20220209182822.640905-1-seanga2@gmail.com/ Fixes: 7224d5ccf8e1 ("pinctrl: Add support for Kendryte K210 FPIOA") Signed-off-by: Niklas Cassel Reviewed-by: Sean Anderson --- drivers/pinctrl/pinctrl-k210.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/pinctrl-k210.c b/drivers/pinctrl/pinctrl-k210.c index f809149102..13f0a34268 100644 --- a/drivers/pinctrl/pinctrl-k210.c +++ b/drivers/pinctrl/pinctrl-k210.c @@ -536,7 +536,7 @@ static int k210_pc_pinconf_set(struct udevice *dev, unsigned pin_selector, break; case PIN_CONFIG_BIAS_PULL_UP: if (argument) - val |= K210_PC_PD; + val |= K210_PC_PU; else return -EINVAL; break; -- cgit v1.2.3 From 351729ca445d4822502ff7117f8213832e753f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Tue, 15 Mar 2022 16:37:27 +0100 Subject: arm: mvebu: dts: turris_mox: fix non-working network / MDIO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 0934dddc6436 ("arm: a37xx: Update DTS files to version from upstream Linux kernel") ported Linux's device-tree files for Armada 3720 SOCs. This broke network on Turris MOX, because the SOC's MDIO bus in U-Boot currently isn't probed via DM as it's own device, but is registered as part of mvneta's driver, which means that pinctrl definitions are not parsed for the MDIO bus node. Also mvneta driver does not consider "phy-handle" property, only "phy". For now, fix this by adding armada-3720-turris-mox-u-boot.dtsi file returning the MDIO to how it was defined previously. A better solution (using proper mvmdio DM driver) is being work on, but will need testing on various boards, and we need the bug fixed now for the upcoming release. Fixes: 0934dddc6436 ("arm: a37xx: Update DTS files to version from upstream Linux kernel") Signed-off-by: Marek Behún Reviewed-by: Stefan Roese --- arch/arm/dts/armada-3720-turris-mox-u-boot.dtsi | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 arch/arm/dts/armada-3720-turris-mox-u-boot.dtsi diff --git a/arch/arm/dts/armada-3720-turris-mox-u-boot.dtsi b/arch/arm/dts/armada-3720-turris-mox-u-boot.dtsi new file mode 100644 index 0000000000..2e05b973d2 --- /dev/null +++ b/arch/arm/dts/armada-3720-turris-mox-u-boot.dtsi @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * 2022 by Marek Behún + */ + +/ { + mdio { + #address-cells = <1>; + #size-cells = <0>; + + old_binding_phy1: ethernet-phy@1 { + reg = <1>; + }; + }; +}; + +ð0 { + pinctrl-0 = <&rgmii_pins>, <&smi_pins>; + /delete-property/ phy-handle; + phy = <&old_binding_phy1>; +}; + +/delete-node/ &mdio; -- cgit v1.2.3 From 925f6900c8f51f2b9d4c5cd389be51c19121e974 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 22 Feb 2022 11:28:18 +0800 Subject: mmc: fsl_esdhc_imx: use VENDORSPEC_FRC_SDCLK_ON when necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After commit f132aab40327 ("Revert "mmc: fsl_esdhc_imx: use VENDORSPEC_FRC_SDCLK_ON to control card clock output""), it involve issue in mmc_switch_voltage(), because of the special design of usdhc. For FSL_USDHC, it do not implement VENDORSPEC_CKEN/PEREN/HCKEN/IPGEN, these are reserved bits(Though RM contain the definition of these bits, but actually internal IC logic do not implement, already confirm with IC team). Instead, use VENDORSPEC_FRC_SDCLK_ON to gate on/off the card clock output. Here is the definition of this bit in RM: [8] FRC_SDCLK_ON Force CLK output active Do not set this bit to 1 unless it is necessary. Also, make sure that this bit is cleared when uSDHC’s clock is about to be changed (frequency change, clock source change, or delay chain tuning). 0b - CLK active or inactive is fully controlled by the hardware. 1b - Force CLK active In default, the FRC_SDCLK_ON is 0. This means, when there is no command or data transfer on bus, hardware will gate off the card clock. But in some case, we need the card clock keep on. Take IO voltage 1.8v switch as example, after IO voltage change to 1.8v, spec require gate off the card clock for 5ms, and gate on the clock back, once detect the card clock on, then the card will draw the dat0 to high immediately. If there is not clock gate off/on behavior, some card will keep the dat0 to low level. This is the reason we fail in mmc_switch_voltage(). To fix this issue, and concern that this is only the fsl usdhc hardware design limitation, set the bit FRC_SDCLK_ON in the beginning of the wait_dat0() and clear it in the end. To make sure the 1.8v IO voltage switch process align with SD specification. For standard tuning process, usdhc specification also require the card clock keep on, so also add these behavior in fsl_esdhc_execute_tuning(). Reviewed-by: Marek Vasut Tested-by: Fabio Estevam Signed-off-by: Haibo Chen Reviewed-by: Peng Fan Reviewed-by: Jaehoon Chung --- drivers/mmc/fsl_esdhc_imx.c | 25 ++++++++++++++++++++++--- include/fsl_esdhc_imx.h | 2 ++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/fsl_esdhc_imx.c b/drivers/mmc/fsl_esdhc_imx.c index 697e3c641d..02208a5ade 100644 --- a/drivers/mmc/fsl_esdhc_imx.c +++ b/drivers/mmc/fsl_esdhc_imx.c @@ -827,13 +827,16 @@ static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode) struct mmc *mmc = &plat->mmc; u32 irqstaten = esdhc_read32(®s->irqstaten); u32 irqsigen = esdhc_read32(®s->irqsigen); - int i, ret = -ETIMEDOUT; - u32 val, mixctrl; + int i, err, ret = -ETIMEDOUT; + u32 val, mixctrl, tmp; /* clock tuning is not needed for upto 52MHz */ if (mmc->clock <= 52000000) return 0; + /* make sure the card clock keep on */ + esdhc_setbits32(®s->vendorspec, VENDORSPEC_FRC_SDCLK_ON); + /* This is readw/writew SDHCI_HOST_CONTROL2 when tuning */ if (priv->flags & ESDHC_FLAG_STD_TUNING) { val = esdhc_read32(®s->autoc12err); @@ -893,6 +896,12 @@ static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode) esdhc_stop_tuning(mmc); + /* change to default setting, let host control the card clock */ + esdhc_clrbits32(®s->vendorspec, VENDORSPEC_FRC_SDCLK_ON); + err = readx_poll_timeout(esdhc_read32, ®s->prsstat, tmp, tmp & PRSSTAT_SDOFF, 100); + if (err) + dev_warn(dev, "card clock not gate off as expect.\n"); + return ret; } #endif @@ -1567,14 +1576,24 @@ static int __maybe_unused fsl_esdhc_set_enhanced_strobe(struct udevice *dev) static int fsl_esdhc_wait_dat0(struct udevice *dev, int state, int timeout_us) { - int ret; + int ret, err; u32 tmp; struct fsl_esdhc_priv *priv = dev_get_priv(dev); struct fsl_esdhc *regs = priv->esdhc_regs; + /* make sure the card clock keep on */ + esdhc_setbits32(®s->vendorspec, VENDORSPEC_FRC_SDCLK_ON); + ret = readx_poll_timeout(esdhc_read32, ®s->prsstat, tmp, !!(tmp & PRSSTAT_DAT0) == !!state, timeout_us); + + /* change to default setting, let host control the card clock */ + esdhc_clrbits32(®s->vendorspec, VENDORSPEC_FRC_SDCLK_ON); + err = readx_poll_timeout(esdhc_read32, ®s->prsstat, tmp, tmp & PRSSTAT_SDOFF, 100); + if (err) + dev_warn(dev, "card clock not gate off as expect.\n"); + return ret; } diff --git a/include/fsl_esdhc_imx.h b/include/fsl_esdhc_imx.h index 2153f29bef..b8efd2a166 100644 --- a/include/fsl_esdhc_imx.h +++ b/include/fsl_esdhc_imx.h @@ -37,6 +37,7 @@ #define VENDORSPEC_HCKEN 0x00001000 #define VENDORSPEC_IPGEN 0x00000800 #define VENDORSPEC_INIT 0x20007809 +#define VENDORSPEC_FRC_SDCLK_ON 0x00000100 #define IRQSTAT 0x0002e030 #define IRQSTAT_DMAE (0x10000000) @@ -94,6 +95,7 @@ #define PRSSTAT_CINS (0x00010000) #define PRSSTAT_BREN (0x00000800) #define PRSSTAT_BWEN (0x00000400) +#define PRSSTAT_SDOFF (0x00000080) #define PRSSTAT_SDSTB (0X00000008) #define PRSSTAT_DLA (0x00000004) #define PRSSTAT_CICHB (0x00000002) -- cgit v1.2.3 From 84191f73847c351019bb50728d28220d91b0aee4 Mon Sep 17 00:00:00 2001 From: Max Merchel Date: Thu, 10 Feb 2022 10:16:39 +0100 Subject: cmd/mmc: fix output of mmc info for e-MMC e-MMC and SD standards differ for some CID fields: - 6 Byte Name - assigned by Manufacturer (SD 5 Byte) - 1 Byte OEM - assigned by Jedec (SD 2 Byte) See e-MMC standard (JEDEC Standard No. 84-B51), 7.2.3 (OID) and 7.2.4 (PNM) Signed-off-by: Max Merchel Signed-off-by: Markus Niebel Reviewed-by: Jaehoon Chung --- cmd/mmc.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/cmd/mmc.c b/cmd/mmc.c index 503dbb6199..7464f8d00c 100644 --- a/cmd/mmc.c +++ b/cmd/mmc.c @@ -22,10 +22,18 @@ static void print_mmcinfo(struct mmc *mmc) printf("Device: %s\n", mmc->cfg->name); printf("Manufacturer ID: %x\n", mmc->cid[0] >> 24); - printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff); - printf("Name: %c%c%c%c%c \n", mmc->cid[0] & 0xff, - (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, - (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff); + if (IS_SD(mmc)) { + printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff); + printf("Name: %c%c%c%c%c \n", mmc->cid[0] & 0xff, + (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, + (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff); + } else { + printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xff); + printf("Name: %c%c%c%c%c%c \n", mmc->cid[0] & 0xff, + (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, + (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff, + (mmc->cid[2] >> 24)); + } printf("Bus Speed: %d\n", mmc->clock); #if CONFIG_IS_ENABLED(MMC_VERBOSE) -- cgit v1.2.3 From 0f3466f52fbacce67e147b9234e6323edff26a6d Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Fri, 11 Mar 2022 19:14:07 +0100 Subject: mmc: xenon_sdhci: remove wait_dat0 SDHCI OP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generic SDHCI driver received support for checking the busy status by polling the DAT[0] level instead of waiting for the worst MMC switch time. Unfortunately, it appears that this does not work for Xenon controllers despite being a part of the standard SDHCI registers and the Armada 3720 datasheet itself telling that BIT(20) is useful for detecting the DAT[0] busy signal. I have tried increasing the timeout value, but I have newer managed to catch DAT_LEVEL bits change from 0 at all. This issue appears to hit most if not all SoC-s supported by Xenon driver, at least A3720, A8040 and CN9130 have non working eMMC currently. So, until a better solution is found drop the wait_dat0 OP for Xenon. I was able to only test it on A3720, but it should work for others as well. Fixes: 40e6f52454fc ("drivers: mmc: Add wait_dat0 support for sdhci driver") Signed-off-by: Robert Marko Reviewed-by: Marek Behún Reviewed-by: Jaehoon Chung Reviewed-by: Stefan Roese --- drivers/mmc/xenon_sdhci.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/xenon_sdhci.c b/drivers/mmc/xenon_sdhci.c index e292f2903d..2f8805096c 100644 --- a/drivers/mmc/xenon_sdhci.c +++ b/drivers/mmc/xenon_sdhci.c @@ -439,6 +439,8 @@ static const struct sdhci_ops xenon_sdhci_ops = { .set_ios_post = xenon_sdhci_set_ios_post }; +static struct dm_mmc_ops xenon_mmc_ops; + static int xenon_sdhci_probe(struct udevice *dev) { struct xenon_sdhci_plat *plat = dev_get_plat(dev); @@ -452,6 +454,9 @@ static int xenon_sdhci_probe(struct udevice *dev) host->mmc->dev = dev; upriv->mmc = host->mmc; + xenon_mmc_ops = sdhci_ops; + xenon_mmc_ops.wait_dat0 = NULL; + /* Set quirks */ host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_32BIT_DMA_ADDR; @@ -568,7 +573,7 @@ U_BOOT_DRIVER(xenon_sdhci_drv) = { .id = UCLASS_MMC, .of_match = xenon_sdhci_ids, .of_to_plat = xenon_sdhci_of_to_plat, - .ops = &sdhci_ops, + .ops = &xenon_mmc_ops, .bind = xenon_sdhci_bind, .probe = xenon_sdhci_probe, .remove = xenon_sdhci_remove, -- cgit v1.2.3 From 2a1d7c635fdede97a05d16067a936bba0a37f908 Mon Sep 17 00:00:00 2001 From: Alper Nebi Yasak Date: Tue, 15 Mar 2022 20:46:26 +0300 Subject: mmc: sdhci: Add HS400 Enhanced Strobe support Delegate setting the Enhanced Strobe configuration to individual drivers if they set a function for it. Return -ENOTSUPP if they do not, like what the MMC uclass does. Signed-off-by: Alper Nebi Yasak Reviewed-by: Jaehoon Chung Reviewed-by: Kever Yang --- drivers/mmc/sdhci.c | 18 ++++++++++++++++++ include/sdhci.h | 12 ++++++++++++ 2 files changed, 30 insertions(+) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 766e4a6b0c..bf989a594f 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -513,6 +513,7 @@ void sdhci_set_uhs_timing(struct sdhci_host *host) reg |= SDHCI_CTRL_UHS_SDR104; break; case MMC_HS_400: + case MMC_HS_400_ES: reg |= SDHCI_CTRL_HS400; break; default: @@ -666,6 +667,7 @@ static int sdhci_set_ios(struct mmc *mmc) mmc->selected_mode == MMC_DDR_52 || mmc->selected_mode == MMC_HS_200 || mmc->selected_mode == MMC_HS_400 || + mmc->selected_mode == MMC_HS_400_ES || mmc->selected_mode == UHS_SDR25 || mmc->selected_mode == UHS_SDR50 || mmc->selected_mode == UHS_SDR104 || @@ -799,6 +801,19 @@ static int sdhci_wait_dat0(struct udevice *dev, int state, return -ETIMEDOUT; } +#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT) +static int sdhci_set_enhanced_strobe(struct udevice *dev) +{ + struct mmc *mmc = mmc_get_mmc_dev(dev); + struct sdhci_host *host = mmc->priv; + + if (host->ops && host->ops->set_enhanced_strobe) + return host->ops->set_enhanced_strobe(host); + + return -ENOTSUPP; +} +#endif + const struct dm_mmc_ops sdhci_ops = { .send_cmd = sdhci_send_command, .set_ios = sdhci_set_ios, @@ -808,6 +823,9 @@ const struct dm_mmc_ops sdhci_ops = { .execute_tuning = sdhci_execute_tuning, #endif .wait_dat0 = sdhci_wait_dat0, +#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT) + .set_enhanced_strobe = sdhci_set_enhanced_strobe, +#endif }; #else static const struct mmc_ops sdhci_ops = { diff --git a/include/sdhci.h b/include/sdhci.h index c8d69f5a63..88f1917480 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -272,6 +272,18 @@ struct sdhci_ops { int (*platform_execute_tuning)(struct mmc *host, u8 opcode); int (*set_delay)(struct sdhci_host *host); int (*deferred_probe)(struct sdhci_host *host); + + /** + * set_enhanced_strobe() - Set HS400 Enhanced Strobe config + * + * This is called after setting the card speed and mode to + * HS400 ES, and should set any host-specific configuration + * necessary for it. + * + * @host: SDHCI host structure + * Return: 0 if successful, -ve on error + */ + int (*set_enhanced_strobe)(struct sdhci_host *host); }; #define ADMA_MAX_LEN 65532 -- cgit v1.2.3 From c35af783172563f6a8b55d45ba244cb2d2f09cce Mon Sep 17 00:00:00 2001 From: Alper Nebi Yasak Date: Tue, 15 Mar 2022 20:46:27 +0300 Subject: rockchip: sdhci: Add HS400 Enhanced Strobe support for RK3399 On RK3399, a register bit must be set to enable Enhanced Strobe. Let the Rockchip SDHCI driver set it when Enhanced Strobe configuration is requested. However, having it set makes the lower-speed modes stop working and makes reinitialization fail, so let it be unset as needed in set_control_reg(). This is mostly ported from Linux's Arasan SDHCI driver which happens to be the underlying IP. (drivers/mmc/host/sdhci-of-arasan.c in Linux tree). Signed-off-by: Alper Nebi Yasak Reviewed-by: Jaehoon Chung Reviewed-by: Kever Yang --- drivers/mmc/rockchip_sdhci.c | 53 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c index b91df05de4..f4d5a59036 100644 --- a/drivers/mmc/rockchip_sdhci.c +++ b/drivers/mmc/rockchip_sdhci.c @@ -42,6 +42,9 @@ ((((x) >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK) ==\ PHYCTRL_DLLRDY_DONE) +#define ARASAN_VENDOR_REGISTER 0x78 +#define ARASAN_VENDOR_ENHANCED_STROBE BIT(0) + /* Rockchip specific Registers */ #define DWCMSHC_EMMC_DLL_CTRL 0x800 #define DWCMSHC_EMMC_DLL_CTRL_RESET BIT(1) @@ -117,6 +120,19 @@ struct sdhci_data { * Return: 0 if successful, -ve on error */ int (*set_ios_post)(struct sdhci_host *host); + + /** + * set_enhanced_strobe() - Set HS400 Enhanced Strobe config + * + * This is the set_enhanced_strobe() SDHCI operation that should + * be used for the hardware this driver data is associated with. + * Normally, this is used to set any host-specific configuration + * necessary for HS400 ES. + * + * @host: SDHCI host structure + * Return: 0 if successful, -ve on error + */ + int (*set_enhanced_strobe)(struct sdhci_host *host); }; static int rk3399_emmc_phy_init(struct udevice *dev) @@ -206,6 +222,21 @@ static int rk3399_emmc_get_phy(struct udevice *dev) return 0; } +static int rk3399_sdhci_set_enhanced_strobe(struct sdhci_host *host) +{ + struct mmc *mmc = host->mmc; + u32 vendor; + + vendor = sdhci_readl(host, ARASAN_VENDOR_REGISTER); + if (mmc->selected_mode == MMC_HS_400_ES) + vendor |= ARASAN_VENDOR_ENHANCED_STROBE; + else + vendor &= ~ARASAN_VENDOR_ENHANCED_STROBE; + sdhci_writel(host, vendor, ARASAN_VENDOR_REGISTER); + + return 0; +} + static void rk3399_sdhci_set_control_reg(struct sdhci_host *host) { struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); @@ -217,6 +248,15 @@ static void rk3399_sdhci_set_control_reg(struct sdhci_host *host) rk3399_emmc_phy_power_off(priv->phy); sdhci_set_control_reg(host); + + /* + * Reinitializing the device tries to set it to lower-speed modes + * first, which fails if the Enhanced Strobe bit is set, making + * the device impossible to use. Set the correct value here to + * let reinitialization attempts succeed. + */ + if (CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)) + rk3399_sdhci_set_enhanced_strobe(host); }; static int rk3399_sdhci_set_ios_post(struct sdhci_host *host) @@ -409,10 +449,22 @@ static int rockchip_sdhci_execute_tuning(struct mmc *mmc, u8 opcode) return ret; } +static int rockchip_sdhci_set_enhanced_strobe(struct sdhci_host *host) +{ + struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); + struct sdhci_data *data = (struct sdhci_data *)dev_get_driver_data(priv->dev); + + if (data->set_enhanced_strobe) + return data->set_enhanced_strobe(host); + + return -ENOTSUPP; +} + static struct sdhci_ops rockchip_sdhci_ops = { .set_ios_post = rockchip_sdhci_set_ios_post, .platform_execute_tuning = &rockchip_sdhci_execute_tuning, .set_control_reg = rockchip_sdhci_set_control_reg, + .set_enhanced_strobe = rockchip_sdhci_set_enhanced_strobe, }; static int rockchip_sdhci_probe(struct udevice *dev) @@ -495,6 +547,7 @@ static const struct sdhci_data rk3399_data = { .emmc_phy_init = rk3399_emmc_phy_init, .set_control_reg = rk3399_sdhci_set_control_reg, .set_ios_post = rk3399_sdhci_set_ios_post, + .set_enhanced_strobe = rk3399_sdhci_set_enhanced_strobe, }; static const struct sdhci_data rk3568_data = { -- cgit v1.2.3 From c48021d184097ea4a1bb6bab8c24653de2477fde Mon Sep 17 00:00:00 2001 From: Alper Nebi Yasak Date: Tue, 15 Mar 2022 20:46:28 +0300 Subject: rockchip: sdhci: Add HS400 Enhanced Strobe support for RK3568 On RK3568, a register bit must be set to enable Enhanced Strobe. However, it appears that the address of this register may differ from vendor to vendor and should be read from the underlying MMC IP. Let the Rockchip SDHCI driver read this address and set the relevant bit when Enhanced Strobe configuration is requested. The IP uses a custom mode select value (0x7) for HS400, use that instead of the common but non-standard SDHCI_CTRL_HS400 value (0x5). Also add some necessary DLL_STRBIN and DLL_TXCLK configuration for HS400. Additionally, a bit signifying that the connected hardware is an eMMC chip must be set to enable Data Strobe for HS400 and HS400ES modes. Also make the driver set this bit as appropriate. This is partly ported from Linux's Synopsys DWC MSHC driver which happens to be the underlying IP. (drivers/mmc/host/sdhci-of-dwcmshc.c in Linux tree). Co-developed-by: Yifeng Zhao Signed-off-by: Yifeng Zhao Signed-off-by: Alper Nebi Yasak Reviewed-by: Kever Yang --- drivers/mmc/rockchip_sdhci.c | 64 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c index f4d5a59036..f3f9d83ba3 100644 --- a/drivers/mmc/rockchip_sdhci.c +++ b/drivers/mmc/rockchip_sdhci.c @@ -22,6 +22,8 @@ #include #include +/* DWCMSHC specific Mode Select value */ +#define DWCMSHC_CTRL_HS400 0x7 /* 400KHz is max freq for card ID etc. Use that as min */ #define EMMC_MIN_FREQ 400000 #define KHz (1000) @@ -45,6 +47,14 @@ #define ARASAN_VENDOR_REGISTER 0x78 #define ARASAN_VENDOR_ENHANCED_STROBE BIT(0) +/* DWC IP vendor area 1 pointer */ +#define DWCMSHC_P_VENDOR_AREA1 0xe8 +#define DWCMSHC_AREA1_MASK GENMASK(11, 0) +/* Offset inside the vendor area 1 */ +#define DWCMSHC_EMMC_CONTROL 0x2c +#define DWCMSHC_CARD_IS_EMMC BIT(0) +#define DWCMSHC_ENHANCED_STROBE BIT(8) + /* Rockchip specific Registers */ #define DWCMSHC_EMMC_DLL_CTRL 0x800 #define DWCMSHC_EMMC_DLL_CTRL_RESET BIT(1) @@ -60,8 +70,14 @@ #define DWCMSHC_EMMC_DLL_INC_VALUE 2 #define DWCMSHC_EMMC_DLL_INC 8 #define DWCMSHC_EMMC_DLL_DLYENA BIT(27) -#define DLL_TXCLK_TAPNUM_DEFAULT 0x10 -#define DLL_STRBIN_TAPNUM_DEFAULT 0x3 +#define DLL_TXCLK_TAPNUM_DEFAULT 0xA + +#define DLL_STRBIN_TAPNUM_DEFAULT 0x8 +#define DLL_STRBIN_TAPNUM_FROM_SW BIT(24) +#define DLL_STRBIN_DELAY_NUM_SEL BIT(26) +#define DLL_STRBIN_DELAY_NUM_OFFSET 16 +#define DLL_STRBIN_DELAY_NUM_DEFAULT 0x16 + #define DLL_TXCLK_TAPNUM_FROM_SW BIT(24) #define DWCMSHC_EMMC_DLL_LOCKED BIT(8) #define DWCMSHC_EMMC_DLL_TIMEOUT BIT(9) @@ -327,7 +343,8 @@ static int rk3568_sdhci_emmc_set_clock(struct sdhci_host *host, unsigned int clo sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_TXCLK); extra = DWCMSHC_EMMC_DLL_DLYENA | - DLL_STRBIN_TAPNUM_DEFAULT; + DLL_STRBIN_TAPNUM_DEFAULT | + DLL_STRBIN_TAPNUM_FROM_SW; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); } else { /* reset the clock phase when the frequency is lower than 100MHz */ @@ -335,7 +352,15 @@ static int rk3568_sdhci_emmc_set_clock(struct sdhci_host *host, unsigned int clo extra = DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK); sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK); - sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_STRBIN); + /* + * Before switching to hs400es mode, the driver will enable + * enhanced strobe first. PHY needs to configure the parameters + * of enhanced strobe first. + */ + extra = DWCMSHC_EMMC_DLL_DLYENA | + DLL_STRBIN_DELAY_NUM_SEL | + DLL_STRBIN_DELAY_NUM_DEFAULT << DLL_STRBIN_DELAY_NUM_OFFSET; + sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); } return 0; @@ -346,11 +371,30 @@ static int rk3568_emmc_get_phy(struct udevice *dev) return 0; } +static int rk3568_sdhci_set_enhanced_strobe(struct sdhci_host *host) +{ + struct mmc *mmc = host->mmc; + u32 vendor; + int reg; + + reg = (sdhci_readl(host, DWCMSHC_P_VENDOR_AREA1) & DWCMSHC_AREA1_MASK) + + DWCMSHC_EMMC_CONTROL; + + vendor = sdhci_readl(host, reg); + if (mmc->selected_mode == MMC_HS_400_ES) + vendor |= DWCMSHC_ENHANCED_STROBE; + else + vendor &= ~DWCMSHC_ENHANCED_STROBE; + sdhci_writel(host, vendor, reg); + + return 0; +} + static int rk3568_sdhci_set_ios_post(struct sdhci_host *host) { struct mmc *mmc = host->mmc; uint clock = mmc->tran_speed; - u32 reg; + u32 reg, vendor_reg; if (!clock) clock = mmc->clock; @@ -360,8 +404,15 @@ static int rk3568_sdhci_set_ios_post(struct sdhci_host *host) if (mmc->selected_mode == MMC_HS_400 || mmc->selected_mode == MMC_HS_400_ES) { reg = sdhci_readw(host, SDHCI_HOST_CONTROL2); reg &= ~SDHCI_CTRL_UHS_MASK; - reg |= SDHCI_CTRL_HS400; + reg |= DWCMSHC_CTRL_HS400; sdhci_writew(host, reg, SDHCI_HOST_CONTROL2); + + vendor_reg = (sdhci_readl(host, DWCMSHC_P_VENDOR_AREA1) & DWCMSHC_AREA1_MASK) + + DWCMSHC_EMMC_CONTROL; + /* set CARD_IS_EMMC bit to enable Data Strobe for HS400 */ + reg = sdhci_readw(host, vendor_reg); + reg |= DWCMSHC_CARD_IS_EMMC; + sdhci_writew(host, reg, vendor_reg); } else { sdhci_set_uhs_timing(host); } @@ -554,6 +605,7 @@ static const struct sdhci_data rk3568_data = { .get_phy = rk3568_emmc_get_phy, .emmc_phy_init = rk3568_emmc_phy_init, .set_ios_post = rk3568_sdhci_set_ios_post, + .set_enhanced_strobe = rk3568_sdhci_set_enhanced_strobe, }; static const struct udevice_id sdhci_ids[] = { -- cgit v1.2.3 From eb0ca6b5ecd8fbbbd55ebdf63a4a622fd6ec907f Mon Sep 17 00:00:00 2001 From: Alper Nebi Yasak Date: Fri, 24 Dec 2021 16:43:43 +0300 Subject: rockchip: gru: Set up SoC IO domain registers The RK3399 SoC needs to know the voltage value provided by some regulators, which is done by setting relevant register bits. Configure these the way other RK3399 boards do, but with the same values as are set in the equivalent code in coreboot. Signed-off-by: Alper Nebi Yasak Reviewed-by: Kever Yang Reviewed-by: Simon Glass Tested-by: Simon Glass --- board/google/gru/gru.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/board/google/gru/gru.c b/board/google/gru/gru.c index 23080c1798..cbf62a9427 100644 --- a/board/google/gru/gru.c +++ b/board/google/gru/gru.c @@ -6,6 +6,17 @@ #include #include #include +#include +#include +#include +#include +#include +#include + +#define GRF_IO_VSEL_BT656_SHIFT 0 +#define GRF_IO_VSEL_AUDIO_SHIFT 1 +#define PMUGRF_CON0_VSEL_SHIFT 8 +#define PMUGRF_CON0_VOL_SHIFT 9 #ifdef CONFIG_SPL_BUILD /* provided to defeat compiler optimisation in board_init_f() */ @@ -54,3 +65,44 @@ int board_early_init_r(void) return 0; } #endif + +static void setup_iodomain(void) +{ + struct rk3399_grf_regs *grf = + syscon_get_first_range(ROCKCHIP_SYSCON_GRF); + struct rk3399_pmugrf_regs *pmugrf = + syscon_get_first_range(ROCKCHIP_SYSCON_PMUGRF); + + /* BT656 and audio is in 1.8v domain */ + rk_setreg(&grf->io_vsel, (1 << GRF_IO_VSEL_BT656_SHIFT | + 1 << GRF_IO_VSEL_AUDIO_SHIFT)); + + /* + * Set GPIO1 1.8v/3.0v source select to PMU1830_VOL + * and explicitly configure that PMU1830_VOL to be 1.8V + */ + rk_setreg(&pmugrf->soc_con0, (1 << PMUGRF_CON0_VSEL_SHIFT | + 1 << PMUGRF_CON0_VOL_SHIFT)); +} + +int misc_init_r(void) +{ + const u32 cpuid_offset = 0x7; + const u32 cpuid_length = 0x10; + u8 cpuid[cpuid_length]; + int ret; + + setup_iodomain(); + + ret = rockchip_cpuid_from_efuse(cpuid_offset, cpuid_length, cpuid); + if (ret) + return ret; + + ret = rockchip_cpuid_set(cpuid, cpuid_length); + if (ret) + return ret; + + ret = rockchip_setup_macaddr(); + + return ret; +} -- cgit v1.2.3 From 8ba1ade3f89f7505250c190e2749ad72422cbbc0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 24 Dec 2021 16:43:44 +0300 Subject: rockchip: gru: Add more devicetree settings This adds some devicetree settings for the Gru-based boards, based on what works on a Kevin board. Gru-based boards usually have an 8MiB SPI flash chip and boot from it. Make the u-boot.rom file intended to be flashed on it match its size. Add properties for booting from SPI, and only try to boot from SPI as MMC and SD card don't seem to work in SPL yet. The Chromium OS EC needs a delay between transactions so it can get itself ready. Also it currently uses a non-standard way of specifying the interrupt. Add these so that the EC works reliably. The Rockchip Embedded DisplayPort driver is looking for a rockchip,panel property to find the panel it should work on. Add the property for the Gru-based boards. The U-Boot GPIO controlled regulator driver only considers the "enable-gpios" devicetree property, not the singular "enable-gpio" one. Some devicetree source files have the singular form as they were added to Linux kernel when it used that form, and imported to U-Boot as is. Fix one instance of this in the Gru boards' devicetree to the form that works in U-Boot. The PWM controlled regulator driver complains that there is no init voltage set for a regulator it drives, though it's not clear which one. Set them all to the voltage levels coreboot sets them: 900 mV. The RK3399 SoC needs to know the voltage level that some supplies provides, including one fixed 1.8V audio-related regulator. Although this synchronization is currently statically done in the board init functions, a not-so-hypothetical driver that does this dynamically would query the regulator only to get -ENODATA and be confused. Make sure U-Boot knows this supply is at 1.8V by setting its limits to that. Most of this is a reapplication of commit 08c85b57a5ec ("rockchip: gru: Add extra device-tree settings") whose changes were removed during a sync with Linux at commit 167efc2c7a46 ("arm64: dts: rk3399: Sync v5.7-rc1 from Linux"). Apply things to rk3399-gru-u-boot.dtsi instead so they don't get lost again. Signed-off-by: Simon Glass [Alper: move to -u-boot.dtsi, rewrite commit message, add more nodes] Co-developed-by: Alper Nebi Yasak Signed-off-by: Alper Nebi Yasak Reviewed-by: Kever Yang Reviewed-by: Simon Glass Tested-by: Simon Glass --- arch/arm/dts/rk3399-gru-u-boot.dtsi | 55 +++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/arch/arm/dts/rk3399-gru-u-boot.dtsi b/arch/arm/dts/rk3399-gru-u-boot.dtsi index 390ac2bb5a..33734e99be 100644 --- a/arch/arm/dts/rk3399-gru-u-boot.dtsi +++ b/arch/arm/dts/rk3399-gru-u-boot.dtsi @@ -5,6 +5,61 @@ #include "rk3399-u-boot.dtsi" +/ { + chosen { + u-boot,spl-boot-order = &spi_flash; + }; + + config { + u-boot,spl-payload-offset = <0x40000>; + }; +}; + +&binman { + rom { + size = <0x800000>; + }; +}; + +&cros_ec { + ec-interrupt = <&gpio0 RK_PA1 GPIO_ACTIVE_LOW>; +}; + +&edp { + rockchip,panel = <&edp_panel>; +}; + +&pp1800_audio { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; +}; + +&ppvar_bigcpu_pwm { + regulator-init-microvolt = <900000>; +}; + +&ppvar_centerlogic_pwm { + regulator-init-microvolt = <900000>; +}; + +&ppvar_gpu_pwm { + regulator-init-microvolt = <900000>; +}; + +&ppvar_litcpu_pwm { + regulator-init-microvolt = <900000>; +}; + +&ppvar_sd_card_io { + enable-gpios = <&gpio2 2 GPIO_ACTIVE_HIGH>; +}; + +&spi5 { + spi-activate-delay = <100>; + spi-max-frequency = <3000000>; + spi-deactivate-delay = <200>; +}; + &spi_flash { u-boot,dm-pre-reloc; }; -- cgit v1.2.3 From e4015661c39383c786e00beec586bddb0cf1541c Mon Sep 17 00:00:00 2001 From: Alper Nebi Yasak Date: Fri, 24 Dec 2021 16:43:45 +0300 Subject: rockchip: bob: Enable more configs This patch enables some configs that should be working on the Bob board, based on what is observed to work on the Kevin board. The Bob board uses an Embedded DisplayPort panel compatible with the simple panel and Rockchip eDP drivers. Its backlight is controlled by the Chromium OS Embedded Controller Pulse Width Modulator. Enable these for the board. Also set VIDEO_ROCKCHIP_MAX_{XRES,YRES} to 1280x800, the resolution of its panel. This had to be done for the Kevin board, but it's untested if this is actually necessary for Bob. The Rockchip video driver needs to assert/deassert some resets, so also enable the reset controller. RESET_ROCKCHIP defaults to y for this board when DM_RESET=y, so it's enough to set that. The Bob board has two USB 3.0 Type-C ports and one USB 2.0 Type-A port on its right side. Enable the configs relevant to USB devices so these can be used. This is despite a known issue with RK3399 boards where USB de-init causes a hang, as there is a known workaround. Some other rk3399-based devices enable support for the SoC's random number generator in commit a475bef5340c ("configs: rk3399: enable rng on firefly/rock960/rockpro64"), as it can provide a KASLR seed when booting using UEFI. Enable it for Bob as well. The default misc_init_r() for Rockchip boards sets cpuid and ethernet MAC address based on e-fuse block. A previous patch extends this on Gru boards to set registers related to SoC IO domains as is necessary on these boards. Enable this function and configs for it on Bob. The microSD card slot on this board (and others based on Gru) is connected to a GPIO controlled regulator (ppvar-sd-card-io), which must be operable by U-Boot. Enable the relevant config option to allow this. Bob boards also use the Winbond W25Q64DW SPI flash chip, enable support for Winbond SPI flash chips in the board config so U-Boot can boot with this chip. Signed-off-by: Alper Nebi Yasak Reviewed-by: Kever Yang Reviewed-by: Simon Glass Tested-by: Simon Glass --- configs/chromebook_bob_defconfig | 22 +++++++++++++++++++++- include/configs/gru.h | 3 +++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/configs/chromebook_bob_defconfig b/configs/chromebook_bob_defconfig index 79a26853a5..3366e76e78 100644 --- a/configs/chromebook_bob_defconfig +++ b/configs/chromebook_bob_defconfig @@ -21,6 +21,7 @@ CONFIG_DEFAULT_FDT_FILE="rockchip/rk3399-gru-bob.dtb" # CONFIG_DISPLAY_CPUINFO is not set CONFIG_DISPLAY_BOARDINFO_LATE=y CONFIG_BOARD_EARLY_INIT_R=y +CONFIG_MISC_INIT_R=y CONFIG_BLOBLIST=y CONFIG_BLOBLIST_ADDR=0x100000 CONFIG_BLOBLIST_SIZE=0x1000 @@ -52,8 +53,9 @@ CONFIG_ROCKCHIP_GPIO=y CONFIG_I2C_CROS_EC_TUNNEL=y CONFIG_SYS_I2C_ROCKCHIP=y CONFIG_I2C_MUX=y -CONFIG_DM_KEYBOARD=y CONFIG_CROS_EC_KEYB=y +CONFIG_MISC=y +CONFIG_ROCKCHIP_EFUSE=y CONFIG_CROS_EC=y CONFIG_CROS_EC_SPI=y CONFIG_PWRSEQ=y @@ -65,13 +67,21 @@ CONFIG_MMC_SDHCI_ROCKCHIP=y CONFIG_SF_DEFAULT_BUS=1 CONFIG_SF_DEFAULT_SPEED=20000000 CONFIG_SPI_FLASH_GIGADEVICE=y +CONFIG_SPI_FLASH_WINBOND=y CONFIG_DM_ETH=y CONFIG_ETH_DESIGNWARE=y CONFIG_GMAC_ROCKCHIP=y +CONFIG_PHY_ROCKCHIP_INNO_USB2=y +CONFIG_PHY_ROCKCHIP_TYPEC=y CONFIG_PMIC_RK8XX=y CONFIG_REGULATOR_PWM=y +CONFIG_DM_REGULATOR_GPIO=y CONFIG_REGULATOR_RK8XX=y +CONFIG_PWM_CROS_EC=y CONFIG_PWM_ROCKCHIP=y +CONFIG_DM_RESET=y +CONFIG_DM_RNG=y +CONFIG_RNG_ROCKCHIP=y CONFIG_DEBUG_UART_SHIFT=2 CONFIG_ROCKCHIP_SPI=y CONFIG_SYSRESET=y @@ -80,11 +90,21 @@ CONFIG_USB_XHCI_HCD=y CONFIG_USB_XHCI_DWC3=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_EHCI_GENERIC=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_GENERIC=y +CONFIG_USB_DWC3=y +CONFIG_USB_KEYBOARD=y CONFIG_USB_HOST_ETHER=y CONFIG_USB_ETHER_ASIX=y CONFIG_USB_ETHER_ASIX88179=y CONFIG_USB_ETHER_MCS7830=y CONFIG_USB_ETHER_RTL8152=y CONFIG_USB_ETHER_SMSC95XX=y +CONFIG_DM_VIDEO=y +CONFIG_DISPLAY=y +CONFIG_VIDEO_ROCKCHIP=y +CONFIG_VIDEO_ROCKCHIP_MAX_XRES=1280 +CONFIG_VIDEO_ROCKCHIP_MAX_YRES=800 +CONFIG_DISPLAY_ROCKCHIP_EDP=y CONFIG_CMD_DHRYSTONE=y CONFIG_ERRNO_STR=y diff --git a/include/configs/gru.h b/include/configs/gru.h index be2dc79968..b1084bb21d 100644 --- a/include/configs/gru.h +++ b/include/configs/gru.h @@ -13,4 +13,7 @@ #include +#define CONFIG_USB_OHCI_NEW +#define CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS 2 + #endif -- cgit v1.2.3 From 6d36e92d28c9d009c786e29623c9558b7652ceda Mon Sep 17 00:00:00 2001 From: "Marty E. Plummer" Date: Fri, 24 Dec 2021 16:43:46 +0300 Subject: rockchip: rk3399: Add support for chromebook_kevin Add support for Kevin, an RK3399-based convertible chromebook that is very similar to Bob. This patch is mostly based on existing support for Bob, with only minor changes for Kevin-specific things. Unlike other Gru boards, coreboot sets Kevin's center logic to 925 mV, so adjust it here in the dts as well. The rk3399-gru-kevin devicetree has an unknown event code reference which has to be defined, set it to the Linux counterpart. The new defconfig is copied from Bob with the diffconfig: DEFAULT_DEVICE_TREE "rk3399-gru-bob" -> "rk3399-gru-kevin" DEFAULT_FDT_FILE "rockchip/rk3399-gru-bob.dtb" -> "rockchip/rk3399-gru-kevin.dtb" VIDEO_ROCKCHIP_MAX_XRES 1280 -> 2400 VIDEO_ROCKCHIP_MAX_YRES 800 -> 1600 +TARGET_CHROMEBOOK_KEVIN y With this Kevin can boot from SPI flash to a usable U-Boot prompt on the display with the keyboard working, but cannot boot into Linux for unknown reasons. eMMC starts in a working state but fails to re-init, microSD card works but at a lower-than-expected speed, USB works but causes a hang on de-init. There are known workarounds to solve eMMC and USB issues. Cc: Marty E. Plummer Cc: Simon Glass [Alper: commit message, resync config with Bob, update MAINTAINERS, add to Rockchip doc, add Kconfig help message, set regulator] Co-developed-by: Alper Nebi Yasak Signed-off-by: Alper Nebi Yasak Reviewed-by: Kever Yang Reviewed-by: Simon Glass Tested-by: Simon Glass --- arch/arm/dts/Makefile | 1 + arch/arm/dts/rk3399-gru-kevin-u-boot.dtsi | 11 +++ arch/arm/mach-rockchip/rk3399/Kconfig | 11 +++ arch/arm/mach-rockchip/rk3399/rk3399.c | 3 +- arch/arm/mach-rockchip/spl.c | 3 +- board/google/gru/Kconfig | 16 ++++ board/google/gru/MAINTAINERS | 8 ++ board/google/gru/gru.c | 2 +- configs/chromebook_kevin_defconfig | 111 ++++++++++++++++++++++++++ doc/board/rockchip/rockchip.rst | 1 + include/dt-bindings/input/linux-event-codes.h | 3 +- 11 files changed, 166 insertions(+), 4 deletions(-) create mode 100644 arch/arm/dts/rk3399-gru-kevin-u-boot.dtsi create mode 100644 configs/chromebook_kevin_defconfig diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 960f1a9fd4..644ba961a2 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -137,6 +137,7 @@ dtb-$(CONFIG_ROCKCHIP_RK3399) += \ rk3399-ficus.dtb \ rk3399-firefly.dtb \ rk3399-gru-bob.dtb \ + rk3399-gru-kevin.dtb \ rk3399-khadas-edge.dtb \ rk3399-khadas-edge-captain.dtb \ rk3399-khadas-edge-v.dtb \ diff --git a/arch/arm/dts/rk3399-gru-kevin-u-boot.dtsi b/arch/arm/dts/rk3399-gru-kevin-u-boot.dtsi new file mode 100644 index 0000000000..c03bd48e95 --- /dev/null +++ b/arch/arm/dts/rk3399-gru-kevin-u-boot.dtsi @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Jagan Teki + */ + +#include "rk3399-gru-u-boot.dtsi" +#include "rk3399-sdram-lpddr3-samsung-4GB-1866.dtsi" + +&ppvar_centerlogic_pwm { + regulator-init-microvolt = <925000>; +}; diff --git a/arch/arm/mach-rockchip/rk3399/Kconfig b/arch/arm/mach-rockchip/rk3399/Kconfig index 17628f9171..0833e083d9 100644 --- a/arch/arm/mach-rockchip/rk3399/Kconfig +++ b/arch/arm/mach-rockchip/rk3399/Kconfig @@ -14,6 +14,17 @@ config TARGET_CHROMEBOOK_BOB display. It includes a Chrome OS EC (Cortex-M3) to provide access to the keyboard and battery functions. +config TARGET_CHROMEBOOK_KEVIN + bool "Samsung Chromebook Plus (RK3399)" + select HAS_ROM + select ROCKCHIP_SPI_IMAGE + help + Kevin is a RK3399-based convertible chromebook. It has two USB 3.0 + Type-C ports, 4GB of SDRAM, WiFi and a 12.3" 2400x1600 display. It + uses its USB ports for both power and external display. It includes + a Chromium OS EC (Cortex-M3) to provide access to the keyboard and + battery functions. + config TARGET_EVB_RK3399 bool "RK3399 evaluation board" help diff --git a/arch/arm/mach-rockchip/rk3399/rk3399.c b/arch/arm/mach-rockchip/rk3399/rk3399.c index d40969c888..01a05599cd 100644 --- a/arch/arm/mach-rockchip/rk3399/rk3399.c +++ b/arch/arm/mach-rockchip/rk3399/rk3399.c @@ -140,7 +140,8 @@ void board_debug_uart_init(void) struct rockchip_gpio_regs * const gpio = (void *)GPIO0_BASE; if (IS_ENABLED(CONFIG_SPL_BUILD) && - IS_ENABLED(CONFIG_TARGET_CHROMEBOOK_BOB)) { + (IS_ENABLED(CONFIG_TARGET_CHROMEBOOK_BOB) || + IS_ENABLED(CONFIG_TARGET_CHROMEBOOK_KEVIN))) { rk_setreg(&grf->io_vsel, 1 << 0); /* diff --git a/arch/arm/mach-rockchip/spl.c b/arch/arm/mach-rockchip/spl.c index 02c40fb37e..7a8db632b8 100644 --- a/arch/arm/mach-rockchip/spl.c +++ b/arch/arm/mach-rockchip/spl.c @@ -56,7 +56,8 @@ u32 spl_boot_device(void) defined(CONFIG_TARGET_CHROMEBIT_MICKEY) || \ defined(CONFIG_TARGET_CHROMEBOOK_MINNIE) || \ defined(CONFIG_TARGET_CHROMEBOOK_SPEEDY) || \ - defined(CONFIG_TARGET_CHROMEBOOK_BOB) + defined(CONFIG_TARGET_CHROMEBOOK_BOB) || \ + defined(CONFIG_TARGET_CHROMEBOOK_KEVIN) return BOOT_DEVICE_SPI; #endif if (CONFIG_IS_ENABLED(ROCKCHIP_BACK_TO_BROM)) diff --git a/board/google/gru/Kconfig b/board/google/gru/Kconfig index 61f7bbca98..1455e1481d 100644 --- a/board/google/gru/Kconfig +++ b/board/google/gru/Kconfig @@ -13,3 +13,19 @@ config BOARD_SPECIFIC_OPTIONS # dummy def_bool y endif + +if TARGET_CHROMEBOOK_KEVIN + +config SYS_BOARD + default "gru" + +config SYS_VENDOR + default "google" + +config SYS_CONFIG_NAME + default "gru" + +config BOARD_SPECIFIC_OPTIONS # dummy + def_bool y + +endif diff --git a/board/google/gru/MAINTAINERS b/board/google/gru/MAINTAINERS index e1cda756b8..53257c52a0 100644 --- a/board/google/gru/MAINTAINERS +++ b/board/google/gru/MAINTAINERS @@ -4,3 +4,11 @@ S: Maintained F: board/google/gru/ F: include/configs/gru.h F: configs/chromebook_bob_defconfig + +CHROMEBOOK KEVIN BOARD +M: Simon Glass +M: Alper Nebi Yasak +S: Maintained +F: board/google/gru/ +F: include/configs/gru.h +F: configs/chromebook_kevin_defconfig diff --git a/board/google/gru/gru.c b/board/google/gru/gru.c index cbf62a9427..fbcf845e87 100644 --- a/board/google/gru/gru.c +++ b/board/google/gru/gru.c @@ -26,7 +26,7 @@ void gru_dummy_function(int i) int board_early_init_f(void) { -# ifdef CONFIG_TARGET_CHROMEBOOK_BOB +# if defined(CONFIG_TARGET_CHROMEBOOK_BOB) || defined(CONFIG_TARGET_CHROMEBOOK_KEVIN) int sum, i; /* diff --git a/configs/chromebook_kevin_defconfig b/configs/chromebook_kevin_defconfig new file mode 100644 index 0000000000..831a6d3822 --- /dev/null +++ b/configs/chromebook_kevin_defconfig @@ -0,0 +1,111 @@ +CONFIG_ARM=y +CONFIG_SKIP_LOWLEVEL_INIT=y +CONFIG_ARCH_ROCKCHIP=y +CONFIG_SYS_TEXT_BASE=0x00200000 +CONFIG_SPL_GPIO=y +CONFIG_NR_DRAM_BANKS=1 +CONFIG_ENV_OFFSET=0x3F8000 +CONFIG_DEFAULT_DEVICE_TREE="rk3399-gru-kevin" +CONFIG_SPL_TEXT_BASE=0xff8c2000 +CONFIG_ROCKCHIP_RK3399=y +CONFIG_ROCKCHIP_BOOT_MODE_REG=0 +CONFIG_ROCKCHIP_SPL_RESERVE_IRAM=0x4000 +# CONFIG_SPL_MMC is not set +CONFIG_TARGET_CHROMEBOOK_KEVIN=y +CONFIG_DEBUG_UART_BASE=0xff1a0000 +CONFIG_DEBUG_UART_CLOCK=24000000 +CONFIG_SPL_SPI_FLASH_SUPPORT=y +CONFIG_SPL_SPI=y +CONFIG_DEBUG_UART=y +CONFIG_SYS_LOAD_ADDR=0x800800 +CONFIG_DEFAULT_FDT_FILE="rockchip/rk3399-gru-kevin.dtb" +# CONFIG_DISPLAY_CPUINFO is not set +CONFIG_DISPLAY_BOARDINFO_LATE=y +CONFIG_BOARD_EARLY_INIT_R=y +CONFIG_MISC_INIT_R=y +CONFIG_BLOBLIST=y +CONFIG_BLOBLIST_SIZE=0x1000 +CONFIG_BLOBLIST_ADDR=0x100000 +CONFIG_HANDOFF=y +# CONFIG_SPL_RAW_IMAGE_SUPPORT is not set +CONFIG_SPL_STACK_R=y +CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN=0x4000 +CONFIG_SPL_SPI_LOAD=y +CONFIG_SYS_SPI_U_BOOT_OFFS=0x40000 +CONFIG_CMD_BOOTZ=y +CONFIG_CMD_GPIO=y +CONFIG_CMD_GPT=y +CONFIG_CMD_I2C=y +CONFIG_CMD_MMC=y +CONFIG_CMD_SF_TEST=y +CONFIG_CMD_SPI=y +CONFIG_CMD_USB=y +# CONFIG_CMD_SETEXPR is not set +CONFIG_CMD_TIME=y +CONFIG_CMD_PMIC=y +CONFIG_CMD_REGULATOR=y +CONFIG_CMD_LOG=y +CONFIG_SPL_OF_CONTROL=y +CONFIG_OF_SPL_REMOVE_PROPS="pinctrl-0 pinctrl-names clock-names interrupt-parent assigned-clocks assigned-clock-rates assigned-clock-parents" +CONFIG_ENV_IS_IN_MMC=y +CONFIG_SYS_RELOC_GD_ENV_ADDR=y +CONFIG_SPL_DM_SEQ_ALIAS=y +CONFIG_ROCKCHIP_GPIO=y +CONFIG_I2C_CROS_EC_TUNNEL=y +CONFIG_SYS_I2C_ROCKCHIP=y +CONFIG_I2C_MUX=y +CONFIG_CROS_EC_KEYB=y +CONFIG_MISC=y +CONFIG_ROCKCHIP_EFUSE=y +CONFIG_CROS_EC=y +CONFIG_CROS_EC_SPI=y +CONFIG_PWRSEQ=y +CONFIG_MMC_PWRSEQ=y +CONFIG_MMC_DW=y +CONFIG_MMC_DW_ROCKCHIP=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_ROCKCHIP=y +CONFIG_SF_DEFAULT_BUS=1 +CONFIG_SF_DEFAULT_SPEED=20000000 +CONFIG_SPI_FLASH_GIGADEVICE=y +CONFIG_SPI_FLASH_WINBOND=y +CONFIG_DM_ETH=y +CONFIG_ETH_DESIGNWARE=y +CONFIG_GMAC_ROCKCHIP=y +CONFIG_PHY_ROCKCHIP_INNO_USB2=y +CONFIG_PHY_ROCKCHIP_TYPEC=y +CONFIG_PMIC_RK8XX=y +CONFIG_REGULATOR_PWM=y +CONFIG_DM_REGULATOR_GPIO=y +CONFIG_REGULATOR_RK8XX=y +CONFIG_PWM_CROS_EC=y +CONFIG_PWM_ROCKCHIP=y +CONFIG_DM_RESET=y +CONFIG_DM_RNG=y +CONFIG_RNG_ROCKCHIP=y +CONFIG_DEBUG_UART_SHIFT=2 +CONFIG_ROCKCHIP_SPI=y +CONFIG_SYSRESET=y +CONFIG_USB=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_DWC3=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_GENERIC=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_GENERIC=y +CONFIG_USB_DWC3=y +CONFIG_USB_KEYBOARD=y +CONFIG_USB_HOST_ETHER=y +CONFIG_USB_ETHER_ASIX=y +CONFIG_USB_ETHER_ASIX88179=y +CONFIG_USB_ETHER_MCS7830=y +CONFIG_USB_ETHER_RTL8152=y +CONFIG_USB_ETHER_SMSC95XX=y +CONFIG_DM_VIDEO=y +CONFIG_DISPLAY=y +CONFIG_VIDEO_ROCKCHIP=y +CONFIG_VIDEO_ROCKCHIP_MAX_XRES=2400 +CONFIG_VIDEO_ROCKCHIP_MAX_YRES=1600 +CONFIG_DISPLAY_ROCKCHIP_EDP=y +CONFIG_CMD_DHRYSTONE=y +CONFIG_ERRNO_STR=y diff --git a/doc/board/rockchip/rockchip.rst b/doc/board/rockchip/rockchip.rst index 144cb98ef9..a75e60b9fa 100644 --- a/doc/board/rockchip/rockchip.rst +++ b/doc/board/rockchip/rockchip.rst @@ -66,6 +66,7 @@ List of mainline supported Rockchip boards: - FriendlyElec NanoPi M4B (nanopi-m4b-rk3399) - FriendlyARM NanoPi NEO4 (nanopi-neo4-rk3399) - Google Bob (chromebook_bob) + - Google Kevin (chromebook_kevin) - Khadas Edge (khadas-edge-rk3399) - Khadas Edge-Captain (khadas-edge-captain-rk3399) - Khadas Edge-V (hadas-edge-v-rk3399) diff --git a/include/dt-bindings/input/linux-event-codes.h b/include/dt-bindings/input/linux-event-codes.h index 87cf351bab..331458c0e7 100644 --- a/include/dt-bindings/input/linux-event-codes.h +++ b/include/dt-bindings/input/linux-event-codes.h @@ -749,7 +749,8 @@ #define SW_ROTATE_LOCK 0x0c /* set = rotate locked/disabled */ #define SW_LINEIN_INSERT 0x0d /* set = inserted */ #define SW_MUTE_DEVICE 0x0e /* set = device disabled */ -#define SW_MAX 0x0f +#define SW_PEN_INSERTED 0x0f /* set = pen inserted */ +#define SW_MAX 0x10 #define SW_CNT (SW_MAX+1) /* -- cgit v1.2.3 From ba366809bc9999a24e974ed2e3e90b87116e5f08 Mon Sep 17 00:00:00 2001 From: Johan Jonker Date: Fri, 24 Dec 2021 18:10:28 +0100 Subject: MAINTAINERS: add rockchip regex for more files and directories The current files and directories with wildcard patterns for Rockchip patches in MAINTAINERS is not always complete. Add the regex for DT related files and a generic regex for catching some other forgotten cases, so that the maintainers receive all Rockchip related patches. Signed-off-by: Johan Jonker Reviewed-by: Simon Glass Reviewed-by: Jagan Teki --- MAINTAINERS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8defd09a64..96582fc677 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -397,6 +397,9 @@ M: Philipp Tomsich M: Kever Yang S: Maintained T: git https://source.denx.de/u-boot/custodians/u-boot-rockchip.git +F: arch/arm/dts/rk3* +F: arch/arm/dts/rockchip* +F: arch/arm/dts/rv1108* F: arch/arm/include/asm/arch-rockchip/ F: arch/arm/mach-rockchip/ F: board/rockchip/ @@ -414,6 +417,7 @@ F: tools/rkcommon.h F: tools/rkimage.c F: tools/rksd.c F: tools/rkspi.c +N: rockchip ARM SAMSUNG M: Minkyu Kang -- cgit v1.2.3 From 5f4cc274737de24d122fd899dbdbbd8cd3a8d742 Mon Sep 17 00:00:00 2001 From: Leonidas-Panagiotis Papadakos Date: Mon, 14 Mar 2022 18:51:49 +0200 Subject: rockchip: rk3328: enable USB mass storage on Renegade This is very useful to access the LibreComputer eMMC as removable storage from a PC (e.g. like so `ums 0 mmc 0`). It has been tested as working on my Renegade board. Signed-off-by: Leonidas P. Papadakos Reviewed-by: Kever Yang --- configs/roc-cc-rk3328_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/roc-cc-rk3328_defconfig b/configs/roc-cc-rk3328_defconfig index cf04bbc768..e3e40a6d64 100644 --- a/configs/roc-cc-rk3328_defconfig +++ b/configs/roc-cc-rk3328_defconfig @@ -36,6 +36,7 @@ CONFIG_CMD_BOOTZ=y CONFIG_CMD_GPT=y CONFIG_CMD_MMC=y CONFIG_CMD_USB=y +CONFIG_CMD_USB_MASS_STORAGE=y # CONFIG_CMD_SETEXPR is not set CONFIG_CMD_TIME=y CONFIG_SPL_OF_CONTROL=y -- cgit v1.2.3 From 9acae5480001f2a5936445668fb7fff82867396d Mon Sep 17 00:00:00 2001 From: Giulio Benetti Date: Mon, 14 Mar 2022 10:09:43 +0100 Subject: rockchip: saradc: remove double semi-colon Remove double semi-colon that has been forgotten while adding the driver. This is only a style fix since it doesn't change the functionality of the driver. Signed-off-by: Giulio Benetti Reviewed-by: Kever Yang --- drivers/adc/rockchip-saradc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/adc/rockchip-saradc.c b/drivers/adc/rockchip-saradc.c index e464d33f22..e0cbab6aa0 100644 --- a/drivers/adc/rockchip-saradc.c +++ b/drivers/adc/rockchip-saradc.c @@ -131,7 +131,7 @@ int rockchip_saradc_of_to_plat(struct udevice *dev) } priv->data = data; - uc_pdata->data_mask = (1 << priv->data->num_bits) - 1;; + uc_pdata->data_mask = (1 << priv->data->num_bits) - 1; uc_pdata->data_format = ADC_DATA_FORMAT_BIN; uc_pdata->data_timeout_us = SARADC_TIMEOUT / 5; uc_pdata->channel_mask = (1 << priv->data->num_channels) - 1; -- cgit v1.2.3 From b42297ba8136c10bbd9b2b2b5f76ad836e456935 Mon Sep 17 00:00:00 2001 From: Alper Nebi Yasak Date: Sat, 29 Jan 2022 18:27:56 +0300 Subject: rockchip: Set default LNX_KRNL_IMG_TEXT_OFFSET_BASE to SYS_TEXT_BASE U-Boot can be chainloaded from vendor firmware on ARM64 chromebooks from a GPT partition (roughly the same as in doc/chromium/chainload.rst), but an appropriate image header must be built-in to the U-Boot binary by enabling LINUX_KERNEL_IMAGE_HEADER. This header has a field for an image load offset from 2MiB alignment which must also be customized through LNX_KRNL_IMG_TEXT_OFFSET_BASE. Set it equal to SYS_TEXT_BASE by default for Rockchip boards, which happens to make this offset zero and works fine on chromebook_kevin both for chainloading and bare-metal use. Signed-off-by: Alper Nebi Yasak Reviewed-by: Simon Glass Reviewed-by: Kever Yang --- arch/arm/mach-rockchip/Kconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/mach-rockchip/Kconfig b/arch/arm/mach-rockchip/Kconfig index 92f35309e4..308dc09b03 100644 --- a/arch/arm/mach-rockchip/Kconfig +++ b/arch/arm/mach-rockchip/Kconfig @@ -390,6 +390,9 @@ config ROCKCHIP_SPI_IMAGE containing U-Boot. The image is built by binman. U-Boot sits near the start of the image. +config LNX_KRNL_IMG_TEXT_OFFSET_BASE + default SYS_TEXT_BASE + source "arch/arm/mach-rockchip/px30/Kconfig" source "arch/arm/mach-rockchip/rk3036/Kconfig" source "arch/arm/mach-rockchip/rk3128/Kconfig" -- cgit v1.2.3 From 861682b596b81f988d522edd4c1c76341de112a2 Mon Sep 17 00:00:00 2001 From: Johan Jonker Date: Wed, 12 Jan 2022 17:32:11 +0100 Subject: rockchip: ram: sdram_rk3x88: replace comma by semicolon A comma at the end of a line gives sometimes strange effects in combination with some code formatters, so replace a comma by a semicolon in the sdram_rk3188.c and sdram_rk3288.c files. Signed-off-by: Johan Jonker Reviewed-by: Simon Glass Reviewed-by: Jagan Teki Reviewed-by: Kever Yang --- drivers/ram/rockchip/sdram_rk3188.c | 2 +- drivers/ram/rockchip/sdram_rk3288.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/ram/rockchip/sdram_rk3188.c b/drivers/ram/rockchip/sdram_rk3188.c index d9ed8adfcf..be8ba4464d 100644 --- a/drivers/ram/rockchip/sdram_rk3188.c +++ b/drivers/ram/rockchip/sdram_rk3188.c @@ -762,7 +762,7 @@ static int sdram_init(struct dram_info *dram, * CS1, n=2 * CS0 & CS1, n = 3 */ - sdram_params->ch[channel].rank = 2, + sdram_params->ch[channel].rank = 2; clrsetbits_le32(&publ->pgcr, 0xF << 18, (sdram_params->ch[channel].rank | 1) << 18); diff --git a/drivers/ram/rockchip/sdram_rk3288.c b/drivers/ram/rockchip/sdram_rk3288.c index f3e4a2808a..227a3cc6a8 100644 --- a/drivers/ram/rockchip/sdram_rk3288.c +++ b/drivers/ram/rockchip/sdram_rk3288.c @@ -862,7 +862,7 @@ static int sdram_init(struct dram_info *dram, * CS1, n=2 * CS0 & CS1, n = 3 */ - sdram_params->ch[channel].rank = 2, + sdram_params->ch[channel].rank = 2; clrsetbits_le32(&publ->pgcr, 0xF << 18, (sdram_params->ch[channel].rank | 1) << 18); -- cgit v1.2.3 From 1840ce5f3860673fd935c458148e9ffd84f62a61 Mon Sep 17 00:00:00 2001 From: Johannes Krottmayer Date: Tue, 1 Mar 2022 04:49:50 +0100 Subject: Makefile: Fix doc path in warning message Fix documentation path in warning message about deprecated device driver. Signed-off-by: Johannes Krottmayer Reviewed-by: Heinrich Schuchardt --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8e2441ecb1..14fd2868eb 100644 --- a/Makefile +++ b/Makefile @@ -1080,7 +1080,7 @@ define deprecated echo >&2 "for $(2)). Please update the board to use"; \ echo >&2 "$(firstword $(1)) before the $(3) release. Failure to"; \ echo >&2 "update by the deadline may result in board removal."; \ - echo >&2 "See doc/driver-model/migration.rst for more info."; \ + echo >&2 "See doc/develop/driver-model/migration.rst for more info."; \ echo >&2 "===================================================="; \ fi; fi @@ -1121,7 +1121,7 @@ ifneq ($(CONFIG_DM),y) @echo >&2 "This board does not use CONFIG_DM. CONFIG_DM will be" @echo >&2 "compulsory starting with the v2020.01 release." @echo >&2 "Failure to update may result in board removal." - @echo >&2 "See doc/driver-model/migration.rst for more info." + @echo >&2 "See doc/develop/driver-model/migration.rst for more info." @echo >&2 "====================================================" endif $(call deprecated,CONFIG_WDT,DM watchdog,v2019.10,\ -- cgit v1.2.3 From 17b8cb635371dc99b1cca26f2a97527c63bdc8a4 Mon Sep 17 00:00:00 2001 From: Johannes Krottmayer Date: Tue, 1 Mar 2022 04:49:51 +0100 Subject: tools: buildman: Fix doc path in warning message Fix documentation path in deprecated warning message about device driver. Signed-off-by: Johannes Krottmayer Reviewed-by: Heinrich Schuchardt --- tools/buildman/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/buildman/test.py b/tools/buildman/test.py index 714bb3e4f9..27287438ee 100644 --- a/tools/buildman/test.py +++ b/tools/buildman/test.py @@ -37,7 +37,7 @@ migration = '''===================== WARNING ====================== This board does not use CONFIG_DM. CONFIG_DM will be compulsory starting with the v2020.01 release. Failure to update may result in board removal. -See doc/driver-model/migration.rst for more info. +See doc/develop/driver-model/migration.rst for more info. ==================================================== ''' -- cgit v1.2.3 From 23f20ef77cb9f9777d05ddc88800d1d38d05202c Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 2 Mar 2022 10:42:21 +0100 Subject: doc: board: amlogic-p20x: fix FIP generation doc The doc used GXL instructions, which couldn't work on GXBB SoCs. Signed-off-by: Neil Armstrong --- doc/board/amlogic/p200.rst | 57 +++++++++++++++++++++------------------------- doc/board/amlogic/p201.rst | 57 +++++++++++++++++++++------------------------- 2 files changed, 52 insertions(+), 62 deletions(-) diff --git a/doc/board/amlogic/p200.rst b/doc/board/amlogic/p200.rst index c3d6441fd3..1c84f1c9d0 100644 --- a/doc/board/amlogic/p200.rst +++ b/doc/board/amlogic/p200.rst @@ -54,44 +54,39 @@ Go back to mainline U-boot source tree then : $ mkdir fip - $ cp $FIPDIR/gxl/bl2.bin fip/ - $ cp $FIPDIR/gxl/acs.bin fip/ - $ cp $FIPDIR/gxl/bl21.bin fip/ - $ cp $FIPDIR/gxl/bl30.bin fip/ - $ cp $FIPDIR/gxl/bl301.bin fip/ - $ cp $FIPDIR/gxl/bl31.img fip/ + $ cp $FIPDIR/gxb/bl2.bin fip/ + $ cp $FIPDIR/gxb/acs.bin fip/ + $ cp $FIPDIR/gxb/bl21.bin fip/ + $ cp $FIPDIR/gxb/bl30.bin fip/ + $ cp $FIPDIR/gxb/bl301.bin fip/ + $ cp $FIPDIR/gxb/bl31.img fip/ $ cp u-boot.bin fip/bl33.bin $ $FIPDIR/blx_fix.sh \ fip/bl30.bin \ - fip/zero_tmp \ - fip/bl30_zero.bin \ - fip/bl301.bin \ - fip/bl301_zero.bin \ - fip/bl30_new.bin \ - bl30 + fip/zero_tmp \ + fip/bl30_zero.bin \ + fip/bl301.bin \ + fip/bl301_zero.bin \ + fip/bl30_new.bin \ + bl30 - $ $FIPDIR/acs_tool.pyc fip/bl2.bin fip/bl2_acs.bin fip/acs.bin 0 + $ python $FIPDIR/acs_tool.pyc fip/bl2.bin fip/bl2_acs.bin fip/acs.bin 0 $ $FIPDIR/blx_fix.sh \ - fip/bl2_acs.bin \ - fip/zero_tmp \ - fip/bl2_zero.bin \ - fip/bl21.bin \ - fip/bl21_zero.bin \ - fip/bl2_new.bin \ - bl2 - - $ $FIPDIR/gxl/aml_encrypt_gxl --bl3enc --input fip/bl30_new.bin - $ $FIPDIR/gxl/aml_encrypt_gxl --bl3enc --input fip/bl31.img - $ $FIPDIR/gxl/aml_encrypt_gxl --bl3enc --input fip/bl33.bin - $ $FIPDIR/gxl/aml_encrypt_gxl --bl2sig --input fip/bl2_new.bin --output fip/bl2.n.bin.sig - $ $FIPDIR/gxl/aml_encrypt_gxl --bootmk \ - --output fip/u-boot.bin \ - --bl2 fip/bl2.n.bin.sig \ - --bl30 fip/bl30_new.bin.enc \ - --bl31 fip/bl31.img.enc \ - --bl33 fip/bl33.bin.enc + fip/bl2_acs.bin \ + fip/zero_tmp \ + fip/bl2_zero.bin \ + fip/bl21.bin \ + fip/bl21_zero.bin \ + fip/bl2_new.bin \ + bl2 + + $ $FIPDIR/fip_create --bl30 fip/bl30_new.bin --bl31 fip/bl31.img --bl33 fip/bl33.bin fip/fip.bin + + $ cat fip/bl2_new.bin fip/fip.bin >fip/boot_new.bin + + $ $FIPDIR/gxb/aml_encrypt_gxb --bootsig --input fip/boot_new.bin --output fip/u-boot.bin and then write the image to SD with: diff --git a/doc/board/amlogic/p201.rst b/doc/board/amlogic/p201.rst index 06da933a2a..a3d451c6e6 100644 --- a/doc/board/amlogic/p201.rst +++ b/doc/board/amlogic/p201.rst @@ -54,44 +54,39 @@ Go back to mainline U-boot source tree then : $ mkdir fip - $ cp $FIPDIR/gxl/bl2.bin fip/ - $ cp $FIPDIR/gxl/acs.bin fip/ - $ cp $FIPDIR/gxl/bl21.bin fip/ - $ cp $FIPDIR/gxl/bl30.bin fip/ - $ cp $FIPDIR/gxl/bl301.bin fip/ - $ cp $FIPDIR/gxl/bl31.img fip/ + $ cp $FIPDIR/gxb/bl2.bin fip/ + $ cp $FIPDIR/gxb/acs.bin fip/ + $ cp $FIPDIR/gxb/bl21.bin fip/ + $ cp $FIPDIR/gxb/bl30.bin fip/ + $ cp $FIPDIR/gxb/bl301.bin fip/ + $ cp $FIPDIR/gxb/bl31.img fip/ $ cp u-boot.bin fip/bl33.bin $ $FIPDIR/blx_fix.sh \ fip/bl30.bin \ - fip/zero_tmp \ - fip/bl30_zero.bin \ - fip/bl301.bin \ - fip/bl301_zero.bin \ - fip/bl30_new.bin \ - bl30 + fip/zero_tmp \ + fip/bl30_zero.bin \ + fip/bl301.bin \ + fip/bl301_zero.bin \ + fip/bl30_new.bin \ + bl30 - $ $FIPDIR/acs_tool.pyc fip/bl2.bin fip/bl2_acs.bin fip/acs.bin 0 + $ python $FIPDIR/acs_tool.pyc fip/bl2.bin fip/bl2_acs.bin fip/acs.bin 0 $ $FIPDIR/blx_fix.sh \ - fip/bl2_acs.bin \ - fip/zero_tmp \ - fip/bl2_zero.bin \ - fip/bl21.bin \ - fip/bl21_zero.bin \ - fip/bl2_new.bin \ - bl2 - - $ $FIPDIR/gxl/aml_encrypt_gxl --bl3enc --input fip/bl30_new.bin - $ $FIPDIR/gxl/aml_encrypt_gxl --bl3enc --input fip/bl31.img - $ $FIPDIR/gxl/aml_encrypt_gxl --bl3enc --input fip/bl33.bin - $ $FIPDIR/gxl/aml_encrypt_gxl --bl2sig --input fip/bl2_new.bin --output fip/bl2.n.bin.sig - $ $FIPDIR/gxl/aml_encrypt_gxl --bootmk \ - --output fip/u-boot.bin \ - --bl2 fip/bl2.n.bin.sig \ - --bl30 fip/bl30_new.bin.enc \ - --bl31 fip/bl31.img.enc \ - --bl33 fip/bl33.bin.enc + fip/bl2_acs.bin \ + fip/zero_tmp \ + fip/bl2_zero.bin \ + fip/bl21.bin \ + fip/bl21_zero.bin \ + fip/bl2_new.bin \ + bl2 + + $ $FIPDIR/fip_create --bl30 fip/bl30_new.bin --bl31 fip/bl31.img --bl33 fip/bl33.bin fip/fip.bin + + $ cat fip/bl2_new.bin fip/fip.bin >fip/boot_new.bin + + $ $FIPDIR/gxb/aml_encrypt_gxb --bootsig --input fip/boot_new.bin --output fip/u-boot.bin and then write the image to SD with: -- cgit v1.2.3 From 4c60512ad961d4d8d3193d1156f6d86fa8bc3e48 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 2 Mar 2022 10:42:22 +0100 Subject: doc: boards: amlogic: Add documentation on pre-generated FIP files It add documentation on licencing & provides links to the amlogic-boot-fip pre-built files collections. Signed-off-by: Neil Armstrong --- doc/board/amlogic/beelink-gtking.rst | 2 + doc/board/amlogic/beelink-gtkingpro.rst | 2 + doc/board/amlogic/index.rst | 8 +++ doc/board/amlogic/jethub-j100.rst | 2 + doc/board/amlogic/jethub-j80.rst | 2 + doc/board/amlogic/khadas-vim.rst | 2 + doc/board/amlogic/khadas-vim2.rst | 2 + doc/board/amlogic/khadas-vim3.rst | 2 + doc/board/amlogic/khadas-vim3l.rst | 2 + doc/board/amlogic/libretech-ac.rst | 2 + doc/board/amlogic/libretech-cc.rst | 2 + doc/board/amlogic/nanopi-k2.rst | 2 + doc/board/amlogic/odroid-c2.rst | 2 + doc/board/amlogic/odroid-c4.rst | 2 + doc/board/amlogic/odroid-n2.rst | 2 + doc/board/amlogic/p200.rst | 2 + doc/board/amlogic/p201.rst | 2 + doc/board/amlogic/p212.rst | 2 + doc/board/amlogic/pre-generated-fip.rst | 93 +++++++++++++++++++++++++++++++++ doc/board/amlogic/radxa-zero.rst | 2 + doc/board/amlogic/s400.rst | 2 + doc/board/amlogic/sei510.rst | 2 + doc/board/amlogic/sei610.rst | 2 + doc/board/amlogic/u200.rst | 2 + doc/board/amlogic/wetek-core2.rst | 2 + 25 files changed, 147 insertions(+) create mode 100644 doc/board/amlogic/pre-generated-fip.rst diff --git a/doc/board/amlogic/beelink-gtking.rst b/doc/board/amlogic/beelink-gtking.rst index 56ce2cb273..2fb50c5f7b 100644 --- a/doc/board/amlogic/beelink-gtking.rst +++ b/doc/board/amlogic/beelink-gtking.rst @@ -44,6 +44,8 @@ https://github.com/LibreELEC/amlogic-boot-fip/tree/master/beelink-s922x NB: Beelink use a common board config for GT-King, GT-King Pro and the GS-King-X model, hence the "beelink-s922x" name. +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `beelink-s922x` + .. code-block:: bash $ wget https://github.com/LibreELEC/amlogic-boot-fip/archive/master.zip diff --git a/doc/board/amlogic/beelink-gtkingpro.rst b/doc/board/amlogic/beelink-gtkingpro.rst index d750351361..07bb04bb36 100644 --- a/doc/board/amlogic/beelink-gtkingpro.rst +++ b/doc/board/amlogic/beelink-gtkingpro.rst @@ -45,6 +45,8 @@ https://github.com/LibreELEC/amlogic-boot-fip/tree/master/beelink-s922x NB: Beelink use a common board config for GT-King, GT-King Pro and the GS-King-X model, hence the "beelink-s922x" name. +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `beelink-s922x` + .. code-block:: bash $ wget https://github.com/LibreELEC/amlogic-boot-fip/archive/master.zip diff --git a/doc/board/amlogic/index.rst b/doc/board/amlogic/index.rst index 189b1efe2b..9ef1440433 100644 --- a/doc/board/amlogic/index.rst +++ b/doc/board/amlogic/index.rst @@ -74,6 +74,14 @@ This matrix concerns the actual source code version. | PCIe (+NVMe) | *N/A* | *N/A* | *N/A* | **Yes** | **Yes** | **Yes** | **Yes** | +-------------------------------+-----------+-----------------+--------------+-------------+------------+-------------+--------------+ +Boot Documentation +------------------ + +.. toctree:: + :maxdepth: 1 + + pre-generated-fip + Board Documentation ------------------- diff --git a/doc/board/amlogic/jethub-j100.rst b/doc/board/amlogic/jethub-j100.rst index 58602787d3..d54519aaef 100644 --- a/doc/board/amlogic/jethub-j100.rst +++ b/doc/board/amlogic/jethub-j100.rst @@ -37,6 +37,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `jethub-j100` + Amlogic doesn't provide sources for the firmware and for tools needed to create the bootloader image, so it is necessary to obtain binaries from the git tree published by the board vendor: diff --git a/doc/board/amlogic/jethub-j80.rst b/doc/board/amlogic/jethub-j80.rst index 6b7bdc78b1..f669a0118d 100644 --- a/doc/board/amlogic/jethub-j80.rst +++ b/doc/board/amlogic/jethub-j80.rst @@ -33,6 +33,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `jethub-j80` + Amlogic doesn't provide sources for the firmware and for tools needed to create the bootloader image, so it is necessary to obtain binaries from the git tree published by the board vendor: diff --git a/doc/board/amlogic/khadas-vim.rst b/doc/board/amlogic/khadas-vim.rst index bbb61c29ef..04025d737c 100644 --- a/doc/board/amlogic/khadas-vim.rst +++ b/doc/board/amlogic/khadas-vim.rst @@ -30,6 +30,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `khadas-vim` + Amlogic doesn't provide sources for the firmware and for tools needed to create the bootloader image, so it is necessary to obtain them from the git tree published by the board vendor: diff --git a/doc/board/amlogic/khadas-vim2.rst b/doc/board/amlogic/khadas-vim2.rst index c57d96d8b0..7ac3bdcbaf 100644 --- a/doc/board/amlogic/khadas-vim2.rst +++ b/doc/board/amlogic/khadas-vim2.rst @@ -31,6 +31,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `khadas-vim2` + Amlogic doesn't provide sources for the firmware and for tools needed to create the bootloader image, so it is necessary to obtain them from the git tree published by the board vendor: diff --git a/doc/board/amlogic/khadas-vim3.rst b/doc/board/amlogic/khadas-vim3.rst index 8b7196d988..73dc32b79b 100644 --- a/doc/board/amlogic/khadas-vim3.rst +++ b/doc/board/amlogic/khadas-vim3.rst @@ -57,6 +57,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `khadas-vim3` + Amlogic doesn't provide sources for the firmware and for tools needed to create the bootloader image, so it is necessary to obtain them from the git tree published by the board vendor: diff --git a/doc/board/amlogic/khadas-vim3l.rst b/doc/board/amlogic/khadas-vim3l.rst index aed8955391..692ab3d21d 100644 --- a/doc/board/amlogic/khadas-vim3l.rst +++ b/doc/board/amlogic/khadas-vim3l.rst @@ -57,6 +57,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `khadas-vim3l` + Amlogic doesn't provide sources for the firmware and for tools needed to create the bootloader image, so it is necessary to obtain them from the git tree published by the board vendor: diff --git a/doc/board/amlogic/libretech-ac.rst b/doc/board/amlogic/libretech-ac.rst index 39bae86d32..7a915f9f26 100644 --- a/doc/board/amlogic/libretech-ac.rst +++ b/doc/board/amlogic/libretech-ac.rst @@ -30,6 +30,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `lafrite` + Amlogic doesn't provide sources for the firmware and for tools needed to create the bootloader image, so it is necessary to obtain them from the git tree published by the board vendor: diff --git a/doc/board/amlogic/libretech-cc.rst b/doc/board/amlogic/libretech-cc.rst index 94c74c5a8b..596ce45dc4 100644 --- a/doc/board/amlogic/libretech-cc.rst +++ b/doc/board/amlogic/libretech-cc.rst @@ -54,6 +54,8 @@ These binaries and the tools required below have been collected and prebuilt for convenience at . These apply to both v1 and v2. +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `lepotato` + Download and extract the libretech-cc release from there, and set FIPDIR to point to the `fip` subdirectory. diff --git a/doc/board/amlogic/nanopi-k2.rst b/doc/board/amlogic/nanopi-k2.rst index 1222ee4e85..76ff874434 100644 --- a/doc/board/amlogic/nanopi-k2.rst +++ b/doc/board/amlogic/nanopi-k2.rst @@ -30,6 +30,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `nanopi-k2` + Amlogic doesn't provide sources for the firmware and for tools needed to create the bootloader image, so it is necessary to obtain them from the git tree published by the board vendor: diff --git a/doc/board/amlogic/odroid-c2.rst b/doc/board/amlogic/odroid-c2.rst index 966c18b36e..8a1be4bf55 100644 --- a/doc/board/amlogic/odroid-c2.rst +++ b/doc/board/amlogic/odroid-c2.rst @@ -30,6 +30,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `odroid-c2` + Amlogic doesn't provide sources for the firmware and for tools needed to create the bootloader image, so it is necessary to obtain them from the git tree published by the board vendor: diff --git a/doc/board/amlogic/odroid-c4.rst b/doc/board/amlogic/odroid-c4.rst index f66d60a54d..b512c6a3d8 100644 --- a/doc/board/amlogic/odroid-c4.rst +++ b/doc/board/amlogic/odroid-c4.rst @@ -34,6 +34,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `odroid-c4` or `odroid-hc4` + Amlogic doesn't provide sources for the firmware and for tools needed to create the bootloader image, so it is necessary to obtain them from the git tree published by the board vendor: diff --git a/doc/board/amlogic/odroid-n2.rst b/doc/board/amlogic/odroid-n2.rst index fe63113230..7aad36e003 100644 --- a/doc/board/amlogic/odroid-n2.rst +++ b/doc/board/amlogic/odroid-n2.rst @@ -29,6 +29,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `odroid-n2` or `odroid-n2-plus` + Amlogic doesn't provide sources for the firmware and for tools needed to create the bootloader image, so it is necessary to obtain them from the git tree published by the board vendor: diff --git a/doc/board/amlogic/p200.rst b/doc/board/amlogic/p200.rst index 1c84f1c9d0..5e7c6b0276 100644 --- a/doc/board/amlogic/p200.rst +++ b/doc/board/amlogic/p200.rst @@ -31,6 +31,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `p200` + Amlogic doesn't provide sources for the firmware and for tools needed to create the bootloader image, so it is necessary to obtain them from the git tree published by the board vendor: diff --git a/doc/board/amlogic/p201.rst b/doc/board/amlogic/p201.rst index a3d451c6e6..2cd236582a 100644 --- a/doc/board/amlogic/p201.rst +++ b/doc/board/amlogic/p201.rst @@ -31,6 +31,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `p201` + Amlogic doesn't provide sources for the firmware and for tools needed to create the bootloader image, so it is necessary to obtain them from the git tree published by the board vendor: diff --git a/doc/board/amlogic/p212.rst b/doc/board/amlogic/p212.rst index e2f3fe313b..c1b73e83b1 100644 --- a/doc/board/amlogic/p212.rst +++ b/doc/board/amlogic/p212.rst @@ -31,6 +31,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `p212` + Amlogic doesn't provide sources for the firmware and for tools needed to create the bootloader image, so it is necessary to obtain them from the git tree published by the board vendor: diff --git a/doc/board/amlogic/pre-generated-fip.rst b/doc/board/amlogic/pre-generated-fip.rst new file mode 100644 index 0000000000..c63ea616b8 --- /dev/null +++ b/doc/board/amlogic/pre-generated-fip.rst @@ -0,0 +1,93 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Pre-Generated FIP file set +========================== + +The Amlogic ARMv8 based SoCs uses a vendor variant of the Trusted Firmware-A +boot architecture. + +You can find documentation on the Trusted Firmware-A architecture on: https://www.trustedfirmware.org/projects/tf-a/ + +The Trusted Firmware-A uses the following boot elements (simplified): + +- BL1: First boot step, implemented in ROM on Amlogic SoCs +- BL2: Second boot step, used to initialize the SoC main clocks & DDR interface. The BL21 and ACS board-specific binaries are "inserted" in the BL32 binary before signing/packaging in order to be flashed on the platform. +- BL30: Amlogic Secure Co-Processor (SCP) firmware used to handle all the system management operations (DVFS, suspend/resume, ...) +- BL301: Amlogic Secure Co-Processor (SCP) board-specific firmware "plug-in" to handle custom DVFS & suspend-resume parameters +- BL31: Initializes the interrupt controller and the system management interface (PSCI) +- BL32 (Optional): Is the Trusted Environment Execution (TEE) Operating System to run secure Trusted Apps, e.g. OP-TEE +- BL33: Is the last non-secure step, usually U-Boot which loads Linux + +Amlogic provides in binary form: + +- bl2.bin +- bl30.bin +- bl30.bin +- bl31.img +- bl32.bin + +And for lastest SoCs, Amlogic also provides the DDR drivers used by the BL2 binary. + +The licence of these files wasn't clear until recently, the currently Amlogic distribution licence +is the following: + +.. code-block:: C + + // Copyright (C) 2018 Amlogic, Inc. All rights reserved. + // + // All information contained herein is Amlogic confidential. + // + // This software is provided to you pursuant to Software License + // Agreement (SLA) with Amlogic Inc ("Amlogic"). This software may be + // used only in accordance with the terms of this agreement. + // + // Redistribution and use in source and binary forms, with or without + // modification is strictly prohibited without prior written permission + // from Amlogic. + // + // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The following files are generated from the Amlogic U-Boot fork: + +- acs.bin: contains the PLL & DDR parameters for the board +- bl301.bin: contains the DVFS & suspend-resume handling code for the board +- bl33.bin: U-boot binary image + +The acs.bin & bl301.bin uses the U-Boot GPL-2.0+ headers & build systems, thus those +are considered issued from GPL-2.0+ source code. + +The tools used to sign & package those binary files are delivered in binary format +for Intel x86-64 and Python 2.x only. + +A collection of pre-built with the corresponding Amlogic binaries for the common +commercially available boards were collected in the https://github.com/LibreELEC/amlogic-boot-fip +repository. + +Using this collection for a commercially available board is very easy. + +Here considering the Libre Computer AML-S905X-CC, which codename is `lepotato`: + +.. code-block:: bash + + $ git clone https://github.com/LibreELEC/amlogic-boot-fip --depth=1 + $ cd amlogic-boot-fip + $ mkdir my-output-dir + $ ./build-fip.sh lepotato /path/to/u-boot/u-boot.bin my-output-dir + +and then write the image to SD with: + +.. code-block:: bash + + $ DEV=/dev/your_sd_device + $ dd if=my-output-dir/u-boot.bin.sd.bin of=$DEV conv=fsync,notrunc bs=512 skip=1 seek=1 + $ dd if=my-output-dir/u-boot.bin.sd.bin of=$DEV conv=fsync,notrunc bs=1 count=444 diff --git a/doc/board/amlogic/radxa-zero.rst b/doc/board/amlogic/radxa-zero.rst index 423403f3c7..f5611f52ec 100644 --- a/doc/board/amlogic/radxa-zero.rst +++ b/doc/board/amlogic/radxa-zero.rst @@ -34,6 +34,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `radxa-zero` + Amlogic does not provide sources for the firmware and for tools needed to create the bootloader image, so it is necessary to obtain them from git trees published by the board vendor: diff --git a/doc/board/amlogic/s400.rst b/doc/board/amlogic/s400.rst index 52c7b27332..c92817b421 100644 --- a/doc/board/amlogic/s400.rst +++ b/doc/board/amlogic/s400.rst @@ -31,6 +31,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `s400` + Amlogic doesn't provide sources for the firmware and for tools needed to create the bootloader image, so it is necessary to obtain them from the git tree published by the board vendor: diff --git a/doc/board/amlogic/sei510.rst b/doc/board/amlogic/sei510.rst index 2d296b1c3c..c55e778494 100644 --- a/doc/board/amlogic/sei510.rst +++ b/doc/board/amlogic/sei510.rst @@ -27,6 +27,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `sei510` + Amlogic doesn't provide sources for the firmware and for tools needed to create the bootloader image, so it is necessary to obtain them from the git tree published by the board vendor: diff --git a/doc/board/amlogic/sei610.rst b/doc/board/amlogic/sei610.rst index 9434e6f023..2d754497cc 100644 --- a/doc/board/amlogic/sei610.rst +++ b/doc/board/amlogic/sei610.rst @@ -29,6 +29,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `sei610` + Amlogic doesn't provide sources for the firmware and for tools needed to create the bootloader image, so it is necessary to obtain them from the git tree published by the board vendor: diff --git a/doc/board/amlogic/u200.rst b/doc/board/amlogic/u200.rst index 5aa3936c28..53213fdb68 100644 --- a/doc/board/amlogic/u200.rst +++ b/doc/board/amlogic/u200.rst @@ -32,6 +32,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `u200` + Amlogic doesn't provide sources for the firmware and for tools needed to create the bootloader image, so it is necessary to obtain them from the git tree published by the board vendor: diff --git a/doc/board/amlogic/wetek-core2.rst b/doc/board/amlogic/wetek-core2.rst index 1012079ded..0147d5fbe2 100644 --- a/doc/board/amlogic/wetek-core2.rst +++ b/doc/board/amlogic/wetek-core2.rst @@ -29,6 +29,8 @@ U-Boot compilation Image creation -------------- +For simplified usage, pleaser refer to :doc:`pre-generated-fip` with codename `wetek-core2` + Amlogic does not provide sources for the firmware or the tools needed to create the bootloader image, and WeTek has not publicly shared the precompiled FIP binaries. However the public Khadas VIM2 sources also -- cgit v1.2.3 From aac01df4f589f31746265fd2b6399ed0a338d51d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Tue, 8 Mar 2022 18:59:56 +0100 Subject: Nokia RX-51: Update documentation about QEMU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add section how to run U-Boot in n900 qemu machine. Signed-off-by: Pali Rohár Signed-off-by: Heinrich Schuchardt --- doc/board/nokia/rx51.rst | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/doc/board/nokia/rx51.rst b/doc/board/nokia/rx51.rst index 941f78e777..061fe7677e 100644 --- a/doc/board/nokia/rx51.rst +++ b/doc/board/nokia/rx51.rst @@ -160,3 +160,60 @@ UBIFS support add following lines into file ``configs/nokia_rx51_defconfig``:: CONFIG_CMD_UBIFS=y CONFIG_MTD_UBI_FASTMAP=y CONFIG_MTD_UBI_FASTMAP_AUTOCONVERT=1 + +Run in QEMU +----------- + +Download and compile Linaro version of qemu which contains ``n900`` qemu +machine. Source code is available in qemu-linaro git repository and the +last working version is at commit 8f8d8e0796efe1a6f34cdd83fb798f3c41217ec1. + +Use following commands to compile ``qemu-system-arm`` binary with ``n900`` +qemu machine support: + +.. code-block:: bash + + git clone https://git.linaro.org/qemu/qemu-linaro.git + cd qemu-linaro + git checkout 8f8d8e0796efe1a6f34cdd83fb798f3c41217ec1 + ./configure --enable-system --target-list=arm-softmmu --disable-werror + make -j4 + cd .. + ln -s qemu-linaro/arm-softmmu/qemu-system-arm . + +Using ``n900`` qemu machine requires proprietary Nokia qemu ``qflasher`` tool +(in reality it is just generator of qemu MTD images) with first stage images +(``xloader-qemu.bin`` and ``secondary-qemu.bin``), similar what is required +on the real HW. License of flasher and images allows non-commercial +redistribution and it is available at maemo.org website: + +.. code-block:: bash + + wget -c http://repository.maemo.org/qemu-n900/qemu-n900.tar.gz + tar -xf qemu-n900.tar.gz + +To generate qemu bootable MTD image ``mtd.img`` from U-Boot binary +``u-boot.bin`` and unpacked first stage images, run following command: + +.. code-block:: bash + + ./qflasher -v -x xloader-qemu.bin -s secondary-qemu.bin -k u-boot.bin -m rx51 -o mtd.img + +Instead of ``u-boot.bin`` binary it is possible to also used combined +U-Boot + kernel binary ``combined.bin``. + +Finally, to boot ``mtd.img`` with graphics display and keyboard with optional +serial console on current terminal, run: + +.. code-block:: bash + + ./qemu-system-arm -M n900 -mtdblock mtd.img -serial /dev/tty + +Additionally it is possible to emulate also eMMC and uSD card by appending +qemu ``-sd`` arguments: + +.. code-block:: bash + + ./qemu-system-arm -M n900 -mtdblock mtd.img -sd emmc.img -sd sd.img -serial /dev/tty + +For more examples, look into the ``test/nokia_rx51_test.sh`` CI testing script. -- cgit v1.2.3 From 2899296e5cbbf7e28f5e43025eda74382f44f4ca Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Wed, 16 Mar 2022 12:12:16 +0100 Subject: doc: uefi: Fix reference to CONFIG_EFI_SECURE_BOOT There is no CONFIG_UEFI_SECURE_BOOT, and there was never any. Signed-off-by: Jan Kiszka Reviewed-by: Heinrich Schuchardt --- doc/develop/uefi/uefi.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst index b7bf135627..fe337c88bd 100644 --- a/doc/develop/uefi/uefi.rst +++ b/doc/develop/uefi/uefi.rst @@ -105,7 +105,7 @@ The UEFI specification[1] defines a secure way of executing UEFI images by verifying a signature (or message digest) of image with certificates. This feature on U-Boot is enabled with:: - CONFIG_UEFI_SECURE_BOOT=y + CONFIG_EFI_SECURE_BOOT=y To make the boot sequence safe, you need to establish a chain of trust; In UEFI secure boot the chain trust is defined by the following UEFI variables -- cgit v1.2.3 From bfffb9f84fdacca12b65048697383d27273d47cb Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Mon, 14 Mar 2022 12:03:54 +0100 Subject: doc: board: raspberrypi: Add documentation Add documentation about the different configuration files for the RaspberryPi board family. Signed-off-by: Matthias Brugger Reviewed-by: Simon Glass Reviewed-by: Heinrich Schuchardt --- doc/board/broadcom/index.rst | 10 +++++++ doc/board/broadcom/raspberrypi.rst | 54 ++++++++++++++++++++++++++++++++++++++ doc/board/index.rst | 1 + 3 files changed, 65 insertions(+) create mode 100644 doc/board/broadcom/index.rst create mode 100644 doc/board/broadcom/raspberrypi.rst diff --git a/doc/board/broadcom/index.rst b/doc/board/broadcom/index.rst new file mode 100644 index 0000000000..4f0e825fef --- /dev/null +++ b/doc/board/broadcom/index.rst @@ -0,0 +1,10 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright (C) 2022 Matthias Brugger + +Broadcom +======== + +.. toctree:: + :maxdepth: 2 + + raspberrypi diff --git a/doc/board/broadcom/raspberrypi.rst b/doc/board/broadcom/raspberrypi.rst new file mode 100644 index 0000000000..1d00b38bb2 --- /dev/null +++ b/doc/board/broadcom/raspberrypi.rst @@ -0,0 +1,54 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright (C) 2022 Matthias Brugger + +Raspberry Pi +============ + +About this +---------- + +This document describes the information about Raspberry Pi boards +and it's usage steps. + +Raspberry Pi boards +------------------- + +List of the supported Rasbperry Pi boards and the corresponding defconfig files: + +32 bit +^^^^^^ + +* rpi_defconfig + - Raspberry Pi +* rpi_0_w_defconfig + - Raspberry Pi 1 + - Raspberry Pi zero +* rpi_2_defconfig + - Raspberry Pi 2 +* rpi_3_32b_defconfig + - Raspberry Pi 3b +* rpi_4_32b_defconfig + - Raspberry Pi 4b + +64 bit +^^^^^^ + +* rpi_3_defconfig + - Raspberry Pi 3b +* rpi_3_b_plus_defconfig + - Raspberry Pi 3b+ +* rpi_4_defconfig + - Raspberry Pi 4b +* rpi_arm64_defconfig + - Raspberry Pi 3b + - Raspberry Pi 3b+ + - Raspberry Pi 4b + - Raspberry Pi 400 + - Raspberry Pi CM 3 + - Raspberry Pi CM 3+ + - Raspberry Pi CM 4 + - Raspberry Pi zero 2 w + +rpi_arm64_defconfig uses the device-tree provided by the firmware instead of +the embedded one. It allows to use the same U-Boot binary to boot different +boards. diff --git a/doc/board/index.rst b/doc/board/index.rst index be9ba4de4d..f7bfc441f7 100644 --- a/doc/board/index.rst +++ b/doc/board/index.rst @@ -14,6 +14,7 @@ Board-specific doc apple/index armltd/index atmel/index + broadcom/index congatec/index coreboot/index emulation/index -- cgit v1.2.3 From 0832dd2900f3ec7bf2ae12866138fc0fd9970168 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sun, 27 Feb 2022 13:18:56 +0100 Subject: efi_loader: Ignore DT when ACPI is on For targets that enable ACPI, we should not pass Device Trees into the payload. However, our distro boot logic always passes the builtin DT as an argument. To make it easy to use ACPI with distro boot, let's just ignore the DT argument to bootefi when ACPI is enabled. That way, we can successfully distro boot payloads on ACPI enabled targets. Signed-off-by: Alexander Graf Reviewed-by: Mark Kettenis Reviewed-by: Heinrich Schuchardt --- cmd/bootefi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 46eebd5ee2..53d9f0e0dc 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -265,8 +265,8 @@ efi_status_t efi_install_fdt(void *fdt) */ #if CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE) if (fdt) { - log_err("ERROR: can't have ACPI table and device tree.\n"); - return EFI_LOAD_ERROR; + log_warning("WARNING: Can't have ACPI table and device tree - ignoring DT.\n"); + return EFI_SUCCESS; } #else bootm_headers_t img = { 0 }; -- cgit v1.2.3 From ff6af6eede200503c58250821d1285f94039eef7 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Wed, 16 Mar 2022 17:13:37 +0200 Subject: efi_loader: Set variable attributes when EFI_BUFFER_TOO_SMALL is returned Starting UEFI Spec 2.8 we must fill in the variable attributes when GetVariable() returns EFI_BUFFER_TOO_SMALL and Attributes is non-NULL. This code was written with 2.7 in mind so let's move the code around a bit and fill in the attributes EFI_BUFFER_TOO_SMALL is returned Signed-off-by: Ilias Apalodimas Reviewed-by: Heinrich Schuchardt --- lib/efi_loader/efi_variable_tee.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c index 58931c4efd..dfef18435d 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -368,7 +368,7 @@ efi_status_t efi_get_variable_int(const u16 *variable_name, efi_uintn_t name_size; efi_uintn_t tmp_dsize; u8 *comm_buf = NULL; - efi_status_t ret; + efi_status_t ret, tmp; if (!variable_name || !vendor || !data_size) { ret = EFI_INVALID_PARAMETER; @@ -407,23 +407,32 @@ efi_status_t efi_get_variable_int(const u16 *variable_name, /* Communicate */ ret = mm_communicate(comm_buf, payload_size); - if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) { - /* Update with reported data size for trimmed case */ - *data_size = var_acc->data_size; - } - if (ret != EFI_SUCCESS) - goto out; - - ret = get_property_int(variable_name, name_size, vendor, &var_property); - if (ret != EFI_SUCCESS) + if (ret != EFI_SUCCESS && ret != EFI_BUFFER_TOO_SMALL) goto out; + /* Update with reported data size for trimmed case */ + *data_size = var_acc->data_size; + /* + * UEFI > 2.7 needs the attributes set even if the buffer is + * smaller + */ if (attributes) { + tmp = get_property_int(variable_name, name_size, vendor, + &var_property); + if (tmp != EFI_SUCCESS) { + ret = tmp; + goto out; + } *attributes = var_acc->attr; - if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) + if (var_property.property & + VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) *attributes |= EFI_VARIABLE_READ_ONLY; } + /* return if ret is EFI_BUFFER_TOO_SMALL */ + if (ret != EFI_SUCCESS) + goto out; + if (data) memcpy(data, (u8 *)var_acc->name + var_acc->name_size, var_acc->data_size); -- cgit v1.2.3 From 8399488672e8717d3aa454f9f67408f96168074f Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 26 Feb 2022 12:10:10 +0100 Subject: efi_loader: export efi_dp_shorten() Rename function shorten_path() to efi_dp_shorten() and export it. Signed-off-by: Heinrich Schuchardt --- include/efi_loader.h | 3 ++- lib/efi_loader/efi_device_path.c | 21 +++++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index 110d8ae79c..1ffcdfc485 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -725,7 +725,8 @@ extern void *efi_bounce_buffer; #define EFI_LOADER_BOUNCE_BUFFER_SIZE (64 * 1024 * 1024) #endif - +/* shorten device path */ +struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp); struct efi_device_path *efi_dp_next(const struct efi_device_path *dp); int efi_dp_match(const struct efi_device_path *a, const struct efi_device_path *b); diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index dc787b4d3d..ddd5f132ec 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -122,20 +122,25 @@ int efi_dp_match(const struct efi_device_path *a, } } -/* +/** + * efi_dp_shorten() - shorten device-path + * * We can have device paths that start with a USB WWID or a USB Class node, * and a few other cases which don't encode the full device path with bus * hierarchy: * - * - MESSAGING:USB_WWID - * - MESSAGING:USB_CLASS - * - MEDIA:FILE_PATH - * - MEDIA:HARD_DRIVE - * - MESSAGING:URI + * * MESSAGING:USB_WWID + * * MESSAGING:USB_CLASS + * * MEDIA:FILE_PATH + * * MEDIA:HARD_DRIVE + * * MESSAGING:URI * * See UEFI spec (section 3.1.2, about short-form device-paths) + * + * @dp: original devie-path + * @Return: shortened device-path or NULL */ -static struct efi_device_path *shorten_path(struct efi_device_path *dp) +struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp) { while (dp) { /* @@ -189,7 +194,7 @@ static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path, } } - obj_dp = shorten_path(efi_dp_next(obj_dp)); + obj_dp = efi_dp_shorten(efi_dp_next(obj_dp)); } while (short_path && obj_dp); } -- cgit v1.2.3 From c409593d0853da646194b0a3d65c8b45fe7cb6d4 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 4 Mar 2022 08:20:00 +0100 Subject: efi_loader: fix efi_dp_find_obj() efi_dp_find_obj() should not return any handle with a partially matching device path but the handle with the maximum matching device path. Signed-off-by: Heinrich Schuchardt --- include/efi_loader.h | 4 +- lib/efi_loader/efi_device_path.c | 110 ++++++++++++++++++++++----------------- 2 files changed, 63 insertions(+), 51 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index 1ffcdfc485..6271d40125 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -730,8 +730,8 @@ struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp); struct efi_device_path *efi_dp_next(const struct efi_device_path *dp); int efi_dp_match(const struct efi_device_path *a, const struct efi_device_path *b); -struct efi_object *efi_dp_find_obj(struct efi_device_path *dp, - struct efi_device_path **rem); +efi_handle_t efi_dp_find_obj(struct efi_device_path *dp, + struct efi_device_path **rem); /* get size of the first device path instance excluding end node */ efi_uintn_t efi_dp_instance_size(const struct efi_device_path *dp); /* size of multi-instance device path excluding end node */ diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index ddd5f132ec..aeb5264820 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -159,69 +159,81 @@ struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp) return dp; } -static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path, - struct efi_device_path **rem) +/** + * find_handle() - find handle by device path + * + * If @rem is provided, the handle with the longest partial match is returned. + * + * @dp: device path to search + * @short_path: use short form device path for matching + * @rem: pointer to receive remaining device path + * Return: matching handle + */ +static efi_handle_t find_handle(struct efi_device_path *dp, bool short_path, + struct efi_device_path **rem) { - struct efi_object *efiobj; - efi_uintn_t dp_size = efi_dp_instance_size(dp); + efi_handle_t handle, best_handle = NULL; + efi_uintn_t len, best_len = 0; + + len = efi_dp_instance_size(dp); - list_for_each_entry(efiobj, &efi_obj_list, link) { + list_for_each_entry(handle, &efi_obj_list, link) { struct efi_handler *handler; - struct efi_device_path *obj_dp; + struct efi_device_path *dp_current; + efi_uintn_t len_current; efi_status_t ret; - ret = efi_search_protocol(efiobj, - &efi_guid_device_path, &handler); + ret = efi_search_protocol(handle, &efi_guid_device_path, + &handler); if (ret != EFI_SUCCESS) continue; - obj_dp = handler->protocol_interface; - - do { - if (efi_dp_match(dp, obj_dp) == 0) { - if (rem) { - /* - * Allow partial matches, but inform - * the caller. - */ - *rem = ((void *)dp) + - efi_dp_instance_size(obj_dp); - return efiobj; - } else { - /* Only return on exact matches */ - if (efi_dp_instance_size(obj_dp) == - dp_size) - return efiobj; - } - } - - obj_dp = efi_dp_shorten(efi_dp_next(obj_dp)); - } while (short_path && obj_dp); + dp_current = handler->protocol_interface; + if (short_path) { + dp_current = efi_dp_shorten(dp_current); + if (!dp_current) + continue; + } + len_current = efi_dp_instance_size(dp_current); + if (rem) { + if (len_current < len) + continue; + } else { + if (len_current != len) + continue; + } + if (memcmp(dp_current, dp, len)) + continue; + if (!rem) + return handle; + if (len_current > best_len) { + best_len = len_current; + best_handle = handle; + *rem = (void*)((u8 *)dp + len_current); + } } - - return NULL; + return best_handle; } -/* - * Find an efiobj from device-path, if 'rem' is not NULL, returns the - * remaining part of the device path after the matched object. +/** + * efi_dp_find_obj() - find handle by device path + * + * If @rem is provided, the handle with the longest partial match is returned. + * + * @dp: device path to search + * @rem: pointer to receive remaining device path + * Return: matching handle */ -struct efi_object *efi_dp_find_obj(struct efi_device_path *dp, - struct efi_device_path **rem) +efi_handle_t efi_dp_find_obj(struct efi_device_path *dp, + struct efi_device_path **rem) { - struct efi_object *efiobj; - - /* Search for an exact match first */ - efiobj = find_obj(dp, false, NULL); - - /* Then for a fuzzy match */ - if (!efiobj) - efiobj = find_obj(dp, false, rem); + efi_handle_t handle; - /* And now for a fuzzy short match */ - if (!efiobj) - efiobj = find_obj(dp, true, rem); + handle = find_handle(dp, false, rem); + if (!handle) + /* Match short form device path */ + handle = find_handle(dp, true, rem); - return efiobj; + return handle; } /* -- cgit v1.2.3 From e46ef1db9e2c87d5aa13a04ea2329b8bae7ea9db Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 19 Mar 2022 06:35:43 +0100 Subject: efi_loader: efi_dp_find_obj() add protocol check Let function efi_dp_find_obj() additionally check if a given protocol is installed on the handle relating to the device-path. Signed-off-by: Heinrich Schuchardt --- include/efi_loader.h | 1 + lib/efi_loader/efi_boottime.c | 2 +- lib/efi_loader/efi_capsule.c | 2 +- lib/efi_loader/efi_device_path.c | 23 ++++++++++++++++------- lib/efi_loader/efi_disk.c | 2 +- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index 6271d40125..1ae47a8713 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -731,6 +731,7 @@ struct efi_device_path *efi_dp_next(const struct efi_device_path *dp); int efi_dp_match(const struct efi_device_path *a, const struct efi_device_path *b); efi_handle_t efi_dp_find_obj(struct efi_device_path *dp, + const efi_guid_t *guid, struct efi_device_path **rem); /* get size of the first device path instance excluding end node */ efi_uintn_t efi_dp_instance_size(const struct efi_device_path *dp); diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index d0f3e05e70..a7bc371f54 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1750,7 +1750,7 @@ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path, info->system_table = &systab; if (device_path) { - info->device_handle = efi_dp_find_obj(device_path, NULL); + info->device_handle = efi_dp_find_obj(device_path, NULL, NULL); dp = efi_dp_append(device_path, file_path); if (!dp) { diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index 613b531b82..011942bbb7 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -680,7 +680,7 @@ static bool device_is_present_and_system_part(struct efi_device_path *dp) { efi_handle_t handle; - handle = efi_dp_find_obj(dp, NULL); + handle = efi_dp_find_obj(dp, NULL, NULL); if (!handle) return false; diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index aeb5264820..0a8802903d 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -160,17 +160,19 @@ struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp) } /** - * find_handle() - find handle by device path + * find_handle() - find handle by device path and installed protocol * * If @rem is provided, the handle with the longest partial match is returned. * * @dp: device path to search + * @guid: GUID of protocol that must be installed on path or NULL * @short_path: use short form device path for matching * @rem: pointer to receive remaining device path * Return: matching handle */ -static efi_handle_t find_handle(struct efi_device_path *dp, bool short_path, - struct efi_device_path **rem) +static efi_handle_t find_handle(struct efi_device_path *dp, + const efi_guid_t *guid, bool short_path, + struct efi_device_path **rem) { efi_handle_t handle, best_handle = NULL; efi_uintn_t len, best_len = 0; @@ -183,6 +185,11 @@ static efi_handle_t find_handle(struct efi_device_path *dp, bool short_path, efi_uintn_t len_current; efi_status_t ret; + if (guid) { + ret = efi_search_protocol(handle, guid, &handler); + if (ret != EFI_SUCCESS) + continue; + } ret = efi_search_protocol(handle, &efi_guid_device_path, &handler); if (ret != EFI_SUCCESS) @@ -195,13 +202,13 @@ static efi_handle_t find_handle(struct efi_device_path *dp, bool short_path, } len_current = efi_dp_instance_size(dp_current); if (rem) { - if (len_current < len) + if (len_current > len) continue; } else { if (len_current != len) continue; } - if (memcmp(dp_current, dp, len)) + if (memcmp(dp_current, dp, len_current)) continue; if (!rem) return handle; @@ -220,18 +227,20 @@ static efi_handle_t find_handle(struct efi_device_path *dp, bool short_path, * If @rem is provided, the handle with the longest partial match is returned. * * @dp: device path to search + * @guid: GUID of protocol that must be installed on path or NULL * @rem: pointer to receive remaining device path * Return: matching handle */ efi_handle_t efi_dp_find_obj(struct efi_device_path *dp, + const efi_guid_t *guid, struct efi_device_path **rem) { efi_handle_t handle; - handle = find_handle(dp, false, rem); + handle = find_handle(dp, guid, false, rem); if (!handle) /* Match short form device path */ - handle = find_handle(dp, true, rem); + handle = find_handle(dp, guid, true, rem); return handle; } diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index 45127d1768..d36a35d94f 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -302,7 +302,7 @@ efi_fs_from_path(struct efi_device_path *full_path) efi_free_pool(file_path); /* Get the EFI object for the partition */ - efiobj = efi_dp_find_obj(device_path, NULL); + efiobj = efi_dp_find_obj(device_path, NULL, NULL); efi_free_pool(device_path); if (!efiobj) return NULL; -- cgit v1.2.3 From 9cdf470274ff30cda3ce89567a69106a27404c23 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 26 Feb 2022 12:05:30 +0100 Subject: efi_loader: support booting via short-form device-path The boot manager must support loading from boot options using a short-form device-path, e.g. one where the first element is a hard drive media path. See '3.1.2 Load Options Processing' in UEFI specification version 2.9. Fixes: 0e074d12393b ("efi_loader: carve out efi_load_image_from_file()") Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_boottime.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index a7bc371f54..5bcb8253ed 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1940,7 +1940,7 @@ efi_status_t efi_load_image_from_path(bool boot_policy, { efi_handle_t device; efi_status_t ret; - struct efi_device_path *dp; + struct efi_device_path *dp, *rem; struct efi_load_file_protocol *load_file_protocol = NULL; efi_uintn_t buffer_size; uint64_t addr, pages; @@ -1951,18 +1951,18 @@ efi_status_t efi_load_image_from_path(bool boot_policy, *size = 0; dp = file_path; - ret = EFI_CALL(efi_locate_device_path( - &efi_simple_file_system_protocol_guid, &dp, &device)); + device = efi_dp_find_obj(dp, NULL, &rem); + ret = efi_search_protocol(device, &efi_simple_file_system_protocol_guid, + NULL); if (ret == EFI_SUCCESS) return efi_load_image_from_file(file_path, buffer, size); - ret = EFI_CALL(efi_locate_device_path( - &efi_guid_load_file_protocol, &dp, &device)); + ret = efi_search_protocol(device, &efi_guid_load_file_protocol, NULL); if (ret == EFI_SUCCESS) { guid = &efi_guid_load_file_protocol; } else if (!boot_policy) { guid = &efi_guid_load_file2_protocol; - ret = EFI_CALL(efi_locate_device_path(guid, &dp, &device)); + ret = efi_search_protocol(device, guid, NULL); } if (ret != EFI_SUCCESS) return EFI_NOT_FOUND; @@ -1971,9 +1971,9 @@ efi_status_t efi_load_image_from_path(bool boot_policy, if (ret != EFI_SUCCESS) return EFI_NOT_FOUND; buffer_size = 0; - ret = load_file_protocol->load_file(load_file_protocol, dp, - boot_policy, &buffer_size, - NULL); + ret = EFI_CALL(load_file_protocol->load_file( + load_file_protocol, rem, boot_policy, + &buffer_size, NULL)); if (ret != EFI_BUFFER_TOO_SMALL) goto out; pages = efi_size_in_pages(buffer_size); @@ -1984,7 +1984,7 @@ efi_status_t efi_load_image_from_path(bool boot_policy, goto out; } ret = EFI_CALL(load_file_protocol->load_file( - load_file_protocol, dp, boot_policy, + load_file_protocol, rem, boot_policy, &buffer_size, (void *)(uintptr_t)addr)); if (ret != EFI_SUCCESS) efi_free_pages(addr, pages); -- cgit v1.2.3 From 63276a569d4832d139206d652207663323df46eb Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 26 Feb 2022 12:10:10 +0100 Subject: efi_loader: use short-form DP for load options The GUID of partitions is sufficient for identification and will stay constant in the lifetime of a boot option. The preceding path of the device-path may change due to changes in the enumeration of devices. Therefore it is preferable to use the short-form of device-paths in load options. Adjust the 'efidebug boot add' command accordingly. Signed-off-by: Heinrich Schuchardt --- cmd/efidebug.c | 70 ++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/cmd/efidebug.c b/cmd/efidebug.c index 401d13cc4c..51e2850d21 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -734,20 +734,20 @@ static int do_efi_show_tables(struct cmd_tbl *cmdtp, int flag, } /** - * create_initrd_dp() - Create a special device for our Boot### option - * - * @dev: Device - * @part: Disk partition - * @file: Filename - * Return: Pointer to the device path or ERR_PTR + * create_initrd_dp() - create a special device for our Boot### option * + * @dev: device + * @part: disk partition + * @file: filename + * @shortform: create short form device path + * Return: pointer to the device path or ERR_PTR */ static struct efi_device_path *create_initrd_dp(const char *dev, const char *part, - const char *file) + const char *file, int shortform) { - struct efi_device_path *tmp_dp = NULL, *tmp_fp = NULL; + struct efi_device_path *tmp_dp = NULL, *tmp_fp = NULL, *short_fp = NULL; struct efi_device_path *initrd_dp = NULL; efi_status_t ret; const struct efi_initrd_dp id_dp = { @@ -771,9 +771,13 @@ struct efi_device_path *create_initrd_dp(const char *dev, const char *part, printf("Cannot create device path for \"%s %s\"\n", part, file); goto out; } + if (shortform) + short_fp = efi_dp_shorten(tmp_fp); + if (!short_fp) + short_fp = tmp_fp; initrd_dp = efi_dp_append((const struct efi_device_path *)&id_dp, - tmp_fp); + short_fp); out: efi_free_pool(tmp_dp); @@ -807,6 +811,7 @@ static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag, size_t label_len, label_len16; u16 *label; struct efi_device_path *device_path = NULL, *file_path = NULL; + struct efi_device_path *fp_free = NULL; struct efi_device_path *final_fp = NULL; struct efi_device_path *initrd_dp = NULL; struct efi_load_option lo; @@ -826,7 +831,18 @@ static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag, argc--; argv++; /* 'add' */ for (; argc > 0; argc--, argv++) { - if (!strcmp(argv[0], "-b")) { + int shortform; + + if (*argv[0] != '-' || strlen(argv[0]) != 2) { + r = CMD_RET_USAGE; + goto out; + } + shortform = 0; + switch (argv[0][1]) { + case 'b': + shortform = 1; + /* fallthrough */ + case 'B': if (argc < 5 || lo.label) { r = CMD_RET_USAGE; goto out; @@ -849,24 +865,33 @@ static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag, /* file path */ ret = efi_dp_from_name(argv[3], argv[4], argv[5], - &device_path, &file_path); + &device_path, &fp_free); if (ret != EFI_SUCCESS) { printf("Cannot create device path for \"%s %s\"\n", argv[3], argv[4]); r = CMD_RET_FAILURE; goto out; } + if (shortform) + file_path = efi_dp_shorten(fp_free); + if (!file_path) + file_path = fp_free; fp_size += efi_dp_size(file_path) + sizeof(struct efi_device_path); argc -= 5; argv += 5; - } else if (!strcmp(argv[0], "-i")) { + break; + case 'i': + shortform = 1; + /* fallthrough */ + case 'I': if (argc < 3 || initrd_dp) { r = CMD_RET_USAGE; goto out; } - initrd_dp = create_initrd_dp(argv[1], argv[2], argv[3]); + initrd_dp = create_initrd_dp(argv[1], argv[2], argv[3], + shortform); if (!initrd_dp) { printf("Cannot add an initrd\n"); r = CMD_RET_FAILURE; @@ -876,7 +901,8 @@ static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag, argv += 3; fp_size += efi_dp_size(initrd_dp) + sizeof(struct efi_device_path); - } else if (!strcmp(argv[0], "-s")) { + break; + case 's': if (argc < 1 || lo.optional_data) { r = CMD_RET_USAGE; goto out; @@ -884,7 +910,8 @@ static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag, lo.optional_data = (const u8 *)argv[1]; argc -= 1; argv += 1; - } else { + break; + default: r = CMD_RET_USAGE; goto out; } @@ -927,7 +954,7 @@ out: efi_free_pool(final_fp); efi_free_pool(initrd_dp); efi_free_pool(device_path); - efi_free_pool(file_path); + efi_free_pool(fp_free); free(lo.label); return r; @@ -1571,12 +1598,11 @@ static int do_efidebug(struct cmd_tbl *cmdtp, int flag, static char efidebug_help_text[] = " - UEFI Shell-like interface to configure UEFI environment\n" "\n" - "efidebug boot add " - "-b