Commit c2a96b7f187f for kernel

commit c2a96b7f187fb6a455836d4a6e113947ff11de97
Merge: b2eed73360df b57d5ffc3ab5
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date:   Thu Jul 25 10:42:22 2024 -0700

    Merge tag 'driver-core-6.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core

    Pull driver core updates from Greg KH:
     "Here is the big set of driver core changes for 6.11-rc1.

      Lots of stuff in here, with not a huge diffstat, but apis are evolving
      which required lots of files to be touched. Highlights of the changes
      in here are:

       - platform remove callback api final fixups (Uwe took many releases
         to get here, finally!)

       - Rust bindings for basic firmware apis and initial driver-core
         interactions.

         It's not all that useful for a "write a whole driver in rust" type
         of thing, but the firmware bindings do help out the phy rust
         drivers, and the driver core bindings give a solid base on which
         others can start their work.

         There is still a long way to go here before we have a multitude of
         rust drivers being added, but it's a great first step.

       - driver core const api changes.

         This reached across all bus types, and there are some fix-ups for
         some not-common bus types that linux-next and 0-day testing shook
         out.

         This work is being done to help make the rust bindings more safe,
         as well as the C code, moving toward the end-goal of allowing us to
         put driver structures into read-only memory. We aren't there yet,
         but are getting closer.

       - minor devres cleanups and fixes found by code inspection

       - arch_topology minor changes

       - other minor driver core cleanups

      All of these have been in linux-next for a very long time with no
      reported problems"

    * tag 'driver-core-6.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (55 commits)
      ARM: sa1100: make match function take a const pointer
      sysfs/cpu: Make crash_hotplug attribute world-readable
      dio: Have dio_bus_match() callback take a const *
      zorro: make match function take a const pointer
      driver core: module: make module_[add|remove]_driver take a const *
      driver core: make driver_find_device() take a const *
      driver core: make driver_[create|remove]_file take a const *
      firmware_loader: fix soundness issue in `request_internal`
      firmware_loader: annotate doctests as `no_run`
      devres: Correct code style for functions that return a pointer type
      devres: Initialize an uninitialized struct member
      devres: Fix memory leakage caused by driver API devm_free_percpu()
      devres: Fix devm_krealloc() wasting memory
      driver core: platform: Switch to use kmemdup_array()
      driver core: have match() callback in struct bus_type take a const *
      MAINTAINERS: add Rust device abstractions to DRIVER CORE
      device: rust: improve safety comments
      MAINTAINERS: add Danilo as FIRMWARE LOADER maintainer
      MAINTAINERS: add Rust FW abstractions to FIRMWARE LOADER
      firmware: rust: improve safety comments
      ...

diff --cc drivers/fsi/fsi-occ.c
index f7157c1d77d8,21d2666c4195..f58b158d097c
--- a/drivers/fsi/fsi-occ.c
+++ b/drivers/fsi/fsi-occ.c
@@@ -718,9 -719,7 +718,7 @@@ static void occ_remove(struct platform_
  	else
  		device_for_each_child(&pdev->dev, NULL, occ_unregister_of_child);

 -	ida_simple_remove(&occ_ida, occ->idx);
 +	ida_free(&occ_ida, occ->idx);
-
- 	return 0;
  }

  static const struct of_device_id occ_match[] = {
diff --cc drivers/gpu/drm/stm/lvds.c
index bfc8cb13fbc5,000000000000..2fa2c81784e9
mode 100644,000000..100644
--- a/drivers/gpu/drm/stm/lvds.c
+++ b/drivers/gpu/drm/stm/lvds.c
@@@ -1,1226 -1,0 +1,1224 @@@
 +// SPDX-License-Identifier: GPL-2.0-only
 +/*
 + * Copyright (C) 2023, STMicroelectronics - All Rights Reserved
 + * Author(s): Raphaël GALLAIS-POU <raphael.gallais-pou@foss.st.com> for STMicroelectronics.
 + */
 +
 +#include <drm/drm_atomic_helper.h>
 +#include <drm/drm_bridge.h>
 +#include <drm/drm_device.h>
 +#include <drm/drm_of.h>
 +#include <drm/drm_panel.h>
 +#include <drm/drm_print.h>
 +#include <drm/drm_probe_helper.h>
 +
 +#include <linux/clk.h>
 +#include <linux/clk-provider.h>
 +#include <linux/io.h>
 +#include <linux/iopoll.h>
 +#include <linux/media-bus-format.h>
 +#include <linux/module.h>
 +#include <linux/of_device.h>
 +#include <linux/platform_device.h>
 +#include <linux/reset.h>
 +
 +/* LVDS Host registers */
 +#define LVDS_CR		0x0000  /* configuration register */
 +#define LVDS_DMLCR0	0x0004  /* data mapping lsb configuration register 0 */
 +#define LVDS_DMMCR0	0x0008  /* data mapping msb configuration register 0 */
 +#define LVDS_DMLCR1	0x000C  /* data mapping lsb configuration register 1 */
 +#define LVDS_DMMCR1	0x0010  /* data mapping msb configuration register 1 */
 +#define LVDS_DMLCR2	0x0014  /* data mapping lsb configuration register 2 */
 +#define LVDS_DMMCR2	0x0018  /* data mapping msb configuration register 2 */
 +#define LVDS_DMLCR3	0x001C  /* data mapping lsb configuration register 3 */
 +#define LVDS_DMMCR3	0x0020  /* data mapping msb configuration register 3 */
 +#define LVDS_DMLCR4	0x0024  /* data mapping lsb configuration register 4 */
 +#define LVDS_DMMCR4	0x0028  /* data mapping msb configuration register 4 */
 +#define LVDS_CDL1CR	0x002C  /* channel distrib link 1 configuration register */
 +#define LVDS_CDL2CR	0x0030  /* channel distrib link 2 configuration register */
 +
 +#define CDL1CR_DEFAULT	0x04321 /* Default value for CDL1CR */
 +#define CDL2CR_DEFAULT	0x59876 /* Default value for CDL2CR */
 +
 +#define LVDS_DMLCR(bit)	(LVDS_DMLCR0 + 0x8 * (bit))
 +#define LVDS_DMMCR(bit)	(LVDS_DMMCR0 + 0x8 * (bit))
 +
 +/* LVDS Wrapper registers */
 +#define LVDS_WCLKCR	0x11B0  /* Wrapper clock control register */
 +
 +#define LVDS_HWCFGR	0x1FF0  /* HW configuration register	*/
 +#define LVDS_VERR	0x1FF4  /* Version register	*/
 +#define LVDS_IPIDR	0x1FF8  /* Identification register	*/
 +#define LVDS_SIDR	0x1FFC  /* Size Identification register	*/
 +
 +/* Bitfield description */
 +#define CR_LVDSEN	BIT(0)  /* LVDS PHY Enable */
 +#define CR_HSPOL	BIT(1)  /* Horizontal Synchronization Polarity */
 +#define CR_VSPOL	BIT(2)  /* Vertical Synchronization Polarity */
 +#define CR_DEPOL	BIT(3)  /* Data Enable Polarity */
 +#define CR_CI		BIT(4)  /* Control Internal (software controlled bit) */
 +#define CR_LKMOD	BIT(5)  /* Link Mode, for both Links */
 +#define CR_LKPHA	BIT(6)  /* Link Phase, for both Links */
 +#define CR_LK1POL	GENMASK(20, 16)  /* Link-1 output Polarity */
 +#define CR_LK2POL	GENMASK(25, 21)  /* Link-2 output Polarity */
 +
 +#define DMMCR_MAP0	GENMASK(4, 0) /* Mapping for bit 0 of datalane x */
 +#define DMMCR_MAP1	GENMASK(9, 5) /* Mapping for bit 1 of datalane x */
 +#define DMMCR_MAP2	GENMASK(14, 10) /* Mapping for bit 2 of datalane x */
 +#define DMMCR_MAP3	GENMASK(19, 15) /* Mapping for bit 3 of datalane x */
 +#define DMLCR_MAP4	GENMASK(4, 0) /* Mapping for bit 4 of datalane x */
 +#define DMLCR_MAP5	GENMASK(9, 5) /* Mapping for bit 5 of datalane x */
 +#define DMLCR_MAP6	GENMASK(14, 10) /* Mapping for bit 6 of datalane x */
 +
 +#define CDLCR_DISTR0	GENMASK(3, 0) /* Channel distribution for lane 0 */
 +#define CDLCR_DISTR1	GENMASK(7, 4) /* Channel distribution for lane 1 */
 +#define CDLCR_DISTR2	GENMASK(11, 8) /* Channel distribution for lane 2 */
 +#define CDLCR_DISTR3	GENMASK(15, 12) /* Channel distribution for lane 3 */
 +#define CDLCR_DISTR4	GENMASK(19, 16) /* Channel distribution for lane 4 */
 +
 +#define PHY_GCR_BIT_CLK_OUT	BIT(0)  /* BIT clock enable */
 +#define PHY_GCR_LS_CLK_OUT	BIT(4)  /* LS clock enable */
 +#define PHY_GCR_DP_CLK_OUT	BIT(8)  /* DP clock enable */
 +#define PHY_GCR_RSTZ		BIT(24) /* LVDS PHY digital reset */
 +#define PHY_GCR_DIV_RSTN	BIT(25) /* Output divider reset */
 +#define PHY_SCR_TX_EN		BIT(16) /* Transmission mode enable */
 +/* Current mode driver enable */
 +#define PHY_CMCR_CM_EN_DL	(BIT(28) | BIT(20) | BIT(12) | BIT(4))
 +#define PHY_CMCR_CM_EN_DL4	BIT(4)
 +/* Bias enable */
 +#define PHY_BCR1_EN_BIAS_DL	(BIT(16) | BIT(12) | BIT(8) | BIT(4) | BIT(0))
 +#define PHY_BCR2_BIAS_EN	BIT(28)
 +/* Voltage mode driver enable */
 +#define PHY_BCR3_VM_EN_DL	(BIT(16) | BIT(12) | BIT(8) | BIT(4) | BIT(0))
 +#define PHY_DCR_POWER_OK	BIT(12)
 +#define PHY_CFGCR_EN_DIG_DL	GENMASK(4, 0) /* LVDS PHY digital lane enable */
 +#define PHY_PLLCR1_PLL_EN	BIT(0) /* LVDS PHY PLL enable */
 +#define PHY_PLLCR1_EN_SD	BIT(1) /* LVDS PHY PLL sigma-delta signal enable */
 +#define PHY_PLLCR1_EN_TWG	BIT(2) /* LVDS PHY PLL triangular wave generator enable */
 +#define PHY_PLLCR1_DIV_EN	BIT(8) /* LVDS PHY PLL dividers enable */
 +#define PHY_PLLCR2_NDIV		GENMASK(25, 16) /* NDIV mask value */
 +#define PHY_PLLCR2_BDIV		GENMASK(9, 0)   /* BDIV mask value */
 +#define PHY_PLLSR_PLL_LOCK	BIT(0) /* LVDS PHY PLL lock status */
 +#define PHY_PLLSDCR1_MDIV	GENMASK(9, 0)   /* MDIV mask value */
 +#define PHY_PLLTESTCR_TDIV	GENMASK(25, 16) /* TDIV mask value */
 +#define PHY_PLLTESTCR_CLK_EN	BIT(0) /* Test clock enable */
 +#define PHY_PLLTESTCR_EN	BIT(8) /* Test divider output enable */
 +
 +#define WCLKCR_SECND_CLKPIX_SEL	BIT(0) /* Pixel clock selection */
 +#define WCLKCR_SRCSEL		BIT(8) /* Source selection for the pixel clock */
 +
 +/* Sleep & timeout for pll lock/unlock */
 +#define SLEEP_US	1000
 +#define TIMEOUT_US	200000
 +
 +/*
 + * The link phase defines whether an ODD pixel is carried over together with
 + * the next EVEN pixel or together with the previous EVEN pixel.
 + *
 + * LVDS_DUAL_LINK_EVEN_ODD_PIXELS (LKPHA = 0)
 + *
 + * ,--------.  ,--------.  ,--------.  ,--------.  ,---------.
 + * | ODD  LK \/ PIXEL  3 \/ PIXEL  1 \/ PIXEL' 1 \/ PIXEL' 3 |
 + * | EVEN LK /\ PIXEL  2 /\ PIXEL' 0 /\ PIXEL' 2 /\ PIXEL' 4 |
 + * `--------'  `--------'  `--------'  `--------'  `---------'
 + *
 + * LVDS_DUAL_LINK_ODD_EVEN_PIXELS (LKPHA = 1)
 + *
 + * ,--------.  ,--------.  ,--------.  ,--------.  ,---------.
 + * | ODD  LK \/ PIXEL  3 \/ PIXEL  1 \/ PIXEL' 1 \/ PIXEL' 3 |
 + * | EVEN LK /\ PIXEL  4 /\ PIXEL  2 /\ PIXEL' 0 /\ PIXEL' 2 |
 + * `--------'  `--------'  `--------'  `--------'  `---------'
 + *
 + */
 +enum lvds_link_type {
 +	LVDS_SINGLE_LINK_PRIMARY = 0,
 +	LVDS_SINGLE_LINK_SECONDARY,
 +	LVDS_DUAL_LINK_EVEN_ODD_PIXELS,
 +	LVDS_DUAL_LINK_ODD_EVEN_PIXELS,
 +};
 +
 +enum lvds_pixel {
 +	PIX_R_0 = 0,
 +	PIX_R_1,
 +	PIX_R_2,
 +	PIX_R_3,
 +	PIX_R_4,
 +	PIX_R_5,
 +	PIX_R_6,
 +	PIX_R_7,
 +	PIX_G_0,
 +	PIX_G_1,
 +	PIX_G_2,
 +	PIX_G_3,
 +	PIX_G_4,
 +	PIX_G_5,
 +	PIX_G_6,
 +	PIX_G_7,
 +	PIX_B_0,
 +	PIX_B_1,
 +	PIX_B_2,
 +	PIX_B_3,
 +	PIX_B_4,
 +	PIX_B_5,
 +	PIX_B_6,
 +	PIX_B_7,
 +	PIX_H_S,
 +	PIX_V_S,
 +	PIX_D_E,
 +	PIX_C_E,
 +	PIX_C_I,
 +	PIX_TOG,
 +	PIX_ONE,
 +	PIX_ZER,
 +};
 +
 +struct phy_reg_offsets {
 +	u32 GCR;	/* Global Control Register	*/
 +	u32 CMCR1;    /* Current Mode Control Register 1 */
 +	u32 CMCR2;    /* Current Mode Control Register 2 */
 +	u32 SCR;      /* Serial Control Register	*/
 +	u32 BCR1;     /* Bias Control Register 1	*/
 +	u32 BCR2;     /* Bias Control Register 2	*/
 +	u32 BCR3;     /* Bias Control Register 3	*/
 +	u32 MPLCR;    /* Monitor PLL Lock Control Register */
 +	u32 DCR;      /* Debug Control Register	*/
 +	u32 SSR1;     /* Spare Status Register 1	*/
 +	u32 CFGCR;    /* Configuration Control Register */
 +	u32 PLLCR1;   /* PLL_MODE 1 Control Register	*/
 +	u32 PLLCR2;   /* PLL_MODE 2 Control Register	*/
 +	u32 PLLSR;    /* PLL Status Register	*/
 +	u32 PLLSDCR1; /* PLL_SD_1 Control Register	*/
 +	u32 PLLSDCR2; /* PLL_SD_2 Control Register	*/
 +	u32 PLLTWGCR1;/* PLL_TWG_1 Control Register	*/
 +	u32 PLLTWGCR2;/* PLL_TWG_2 Control Register	*/
 +	u32 PLLCPCR;  /* PLL_CP Control Register	*/
 +	u32 PLLTESTCR;/* PLL_TEST Control Register	*/
 +};
 +
 +struct lvds_phy_info {
 +	u32 base;
 +	struct phy_reg_offsets ofs;
 +};
 +
 +static struct lvds_phy_info lvds_phy_16ff_primary = {
 +	.base = 0x1000,
 +	.ofs = {
 +		.GCR = 0x0,
 +		.CMCR1 = 0xC,
 +		.CMCR2 = 0x10,
 +		.SCR = 0x20,
 +		.BCR1 = 0x2C,
 +		.BCR2 = 0x30,
 +		.BCR3 = 0x34,
 +		.MPLCR = 0x64,
 +		.DCR = 0x84,
 +		.SSR1 = 0x88,
 +		.CFGCR = 0xA0,
 +		.PLLCR1 = 0xC0,
 +		.PLLCR2 = 0xC4,
 +		.PLLSR = 0xC8,
 +		.PLLSDCR1 = 0xCC,
 +		.PLLSDCR2 = 0xD0,
 +		.PLLTWGCR1 = 0xD4,
 +		.PLLTWGCR2 = 0xD8,
 +		.PLLCPCR = 0xE0,
 +		.PLLTESTCR = 0xE8,
 +	}
 +};
 +
 +static struct lvds_phy_info lvds_phy_16ff_secondary = {
 +	.base = 0x1100,
 +	.ofs = {
 +		.GCR = 0x0,
 +		.CMCR1 = 0xC,
 +		.CMCR2 = 0x10,
 +		.SCR = 0x20,
 +		.BCR1 = 0x2C,
 +		.BCR2 = 0x30,
 +		.BCR3 = 0x34,
 +		.MPLCR = 0x64,
 +		.DCR = 0x84,
 +		.SSR1 = 0x88,
 +		.CFGCR = 0xA0,
 +		.PLLCR1 = 0xC0,
 +		.PLLCR2 = 0xC4,
 +		.PLLSR = 0xC8,
 +		.PLLSDCR1 = 0xCC,
 +		.PLLSDCR2 = 0xD0,
 +		.PLLTWGCR1 = 0xD4,
 +		.PLLTWGCR2 = 0xD8,
 +		.PLLCPCR = 0xE0,
 +		.PLLTESTCR = 0xE8,
 +	}
 +};
 +
 +struct stm_lvds {
 +	void __iomem *base;
 +	struct device *dev;
 +	struct clk *pclk;		/* APB peripheral clock */
 +	struct clk *pllref_clk;		/* Reference clock for the internal PLL */
 +	struct clk_hw lvds_ck_px;	/* Pixel clock */
 +	u32 pixel_clock_rate;		/* Pixel clock rate */
 +
 +	struct lvds_phy_info *primary;
 +	struct lvds_phy_info *secondary;
 +
 +	struct drm_bridge lvds_bridge;
 +	struct drm_bridge *next_bridge;
 +	struct drm_connector connector;
 +	struct drm_encoder *encoder;
 +	struct drm_panel *panel;
 +
 +	u32 hw_version;
 +	u32 link_type;
 +};
 +
 +#define bridge_to_stm_lvds(b) \
 +	container_of(b, struct stm_lvds, lvds_bridge)
 +
 +#define connector_to_stm_lvds(c) \
 +	container_of(c, struct stm_lvds, connector)
 +
 +#define lvds_is_dual_link(lvds) \
 +	({	\
 +	typeof(lvds) __lvds = (lvds);	\
 +	__lvds == LVDS_DUAL_LINK_EVEN_ODD_PIXELS ||	\
 +	__lvds == LVDS_DUAL_LINK_ODD_EVEN_PIXELS;	\
 +	})
 +
 +static inline void lvds_write(struct stm_lvds *lvds, u32 reg, u32 val)
 +{
 +	writel(val, lvds->base + reg);
 +}
 +
 +static inline u32 lvds_read(struct stm_lvds *lvds, u32 reg)
 +{
 +	return readl(lvds->base + reg);
 +}
 +
 +static inline void lvds_set(struct stm_lvds *lvds, u32 reg, u32 mask)
 +{
 +	lvds_write(lvds, reg, lvds_read(lvds, reg) | mask);
 +}
 +
 +static inline void lvds_clear(struct stm_lvds *lvds, u32 reg, u32 mask)
 +{
 +	lvds_write(lvds, reg, lvds_read(lvds, reg) & ~mask);
 +}
 +
 +/*
 + * Expected JEIDA-RGB888 data to be sent in LSB format
 + *	    bit6 ............................bit0
 + * CHAN0   {ONE, ONE, ZERO, ZERO, ZERO, ONE, ONE}
 + * CHAN1   {G2,  R7,  R6,   R5,   R4,   R3,  R2}
 + * CHAN2   {B3,  B2,  G7,   G6,   G5,   G4,  G3}
 + * CHAN3   {DE,  VS,  HS,   B7,   B6,   B5,  B4}
 + * CHAN4   {CE,  B1,  B0,   G1,   G0,   R1,  R0}
 + */
 +static enum lvds_pixel lvds_bitmap_jeida_rgb888[5][7] = {
 +	{ PIX_ONE, PIX_ONE, PIX_ZER, PIX_ZER, PIX_ZER, PIX_ONE, PIX_ONE },
 +	{ PIX_G_2, PIX_R_7, PIX_R_6, PIX_R_5, PIX_R_4, PIX_R_3, PIX_R_2 },
 +	{ PIX_B_3, PIX_B_2, PIX_G_7, PIX_G_6, PIX_G_5, PIX_G_4, PIX_G_3 },
 +	{ PIX_D_E, PIX_V_S, PIX_H_S, PIX_B_7, PIX_B_6, PIX_B_5, PIX_B_4 },
 +	{ PIX_C_E, PIX_B_1, PIX_B_0, PIX_G_1, PIX_G_0, PIX_R_1, PIX_R_0 }
 +};
 +
 +/*
 + * Expected VESA-RGB888 data to be sent in LSB format
 + *	    bit6 ............................bit0
 + * CHAN0   {ONE, ONE, ZERO, ZERO, ZERO, ONE, ONE}
 + * CHAN1   {G0,  R5,  R4,   R3,   R2,   R1,  R0}
 + * CHAN2   {B1,  B0,  G5,   G4,   G3,   G2,  G1}
 + * CHAN3   {DE,  VS,  HS,   B5,   B4,   B3,  B2}
 + * CHAN4   {CE,  B7,  B6,   G7,   G6,   R7,  R6}
 + */
 +static enum lvds_pixel lvds_bitmap_vesa_rgb888[5][7] = {
 +	{ PIX_ONE, PIX_ONE, PIX_ZER, PIX_ZER, PIX_ZER, PIX_ONE, PIX_ONE },
 +	{ PIX_G_0, PIX_R_5, PIX_R_4, PIX_R_3, PIX_R_2, PIX_R_1, PIX_R_0 },
 +	{ PIX_B_1, PIX_B_0, PIX_G_5, PIX_G_4, PIX_G_3, PIX_G_2, PIX_G_1 },
 +	{ PIX_D_E, PIX_V_S, PIX_H_S, PIX_B_5, PIX_B_4, PIX_B_3, PIX_B_2 },
 +	{ PIX_C_E, PIX_B_7, PIX_B_6, PIX_G_7, PIX_G_6, PIX_R_7, PIX_R_6 }
 +};
 +
 +/*
 + * Clocks and PHY related functions
 + */
 +static int lvds_pll_enable(struct stm_lvds *lvds, struct lvds_phy_info *phy)
 +{
 +	struct drm_device *drm = lvds->lvds_bridge.dev;
 +	u32 lvds_gcr;
 +	int val, ret;
 +
 +	/*
 +	 * PLL lock timing control for the monitor unmask after startup (pll_en)
 +	 * Adjusted value so that the masking window is opened at start-up
 +	 */
 +	lvds_write(lvds, phy->base + phy->ofs.MPLCR, (0x200 - 0x160) << 16);
 +
 +	/* Enable bias */
 +	lvds_write(lvds, phy->base + phy->ofs.BCR2, PHY_BCR2_BIAS_EN);
 +
 +	/* Enable DP, LS, BIT clock output */
 +	lvds_gcr = PHY_GCR_DP_CLK_OUT | PHY_GCR_LS_CLK_OUT | PHY_GCR_BIT_CLK_OUT;
 +	lvds_set(lvds, phy->base + phy->ofs.GCR, lvds_gcr);
 +
 +	/* Power up all output dividers */
 +	lvds_set(lvds, phy->base + phy->ofs.PLLTESTCR, PHY_PLLTESTCR_EN);
 +	lvds_set(lvds, phy->base + phy->ofs.PLLCR1, PHY_PLLCR1_DIV_EN);
 +
 +	/* Set PHY in serial transmission mode */
 +	lvds_set(lvds, phy->base + phy->ofs.SCR, PHY_SCR_TX_EN);
 +
 +	/* Enable the LVDS PLL & wait for its lock */
 +	lvds_set(lvds, phy->base + phy->ofs.PLLCR1, PHY_PLLCR1_PLL_EN);
 +	ret = readl_poll_timeout_atomic(lvds->base + phy->base + phy->ofs.PLLSR,
 +					val, val & PHY_PLLSR_PLL_LOCK,
 +					SLEEP_US, TIMEOUT_US);
 +	if (ret)
 +		drm_err(drm, "!TIMEOUT! waiting PLL, let's continue\n");
 +
 +	/* WCLKCR_SECND_CLKPIX_SEL is for dual link */
 +	lvds_write(lvds, LVDS_WCLKCR, WCLKCR_SECND_CLKPIX_SEL);
 +
 +	lvds_set(lvds, phy->ofs.PLLTESTCR, PHY_PLLTESTCR_CLK_EN);
 +
 +	return ret;
 +}
 +
 +static int pll_get_clkout_khz(int clkin_khz, int bdiv, int mdiv, int ndiv)
 +{
 +	int divisor = ndiv * bdiv;
 +
 +	/* Prevents from division by 0 */
 +	if (!divisor)
 +		return 0;
 +
 +	return clkin_khz * mdiv / divisor;
 +}
 +
 +#define TDIV	70
 +#define NDIV_MIN	2
 +#define NDIV_MAX	6
 +#define BDIV_MIN	2
 +#define BDIV_MAX	6
 +#define MDIV_MIN	1
 +#define MDIV_MAX	1023
 +
 +static int lvds_pll_get_params(struct stm_lvds *lvds,
 +			       unsigned int clkin_khz, unsigned int clkout_khz,
 +			       unsigned int *bdiv, unsigned int *mdiv, unsigned int *ndiv)
 +{
 +	int delta, best_delta; /* all in khz */
 +	int i, o, n;
 +
 +	/* Early checks preventing division by 0 & odd results */
 +	if (clkin_khz <= 0 || clkout_khz <= 0)
 +		return -EINVAL;
 +
 +	best_delta = 1000000; /* big started value (1000000khz) */
 +
 +	for (i = NDIV_MIN; i <= NDIV_MAX; i++) {
 +		for (o = BDIV_MIN; o <= BDIV_MAX; o++) {
 +			n = DIV_ROUND_CLOSEST(i * o * clkout_khz, clkin_khz);
 +			/* Check ndiv according to vco range */
 +			if (n < MDIV_MIN || n > MDIV_MAX)
 +				continue;
 +			/* Check if new delta is better & saves parameters */
 +			delta = pll_get_clkout_khz(clkin_khz, i, n, o) - clkout_khz;
 +			if (delta < 0)
 +				delta = -delta;
 +			if (delta < best_delta) {
 +				*ndiv = i;
 +				*mdiv = n;
 +				*bdiv = o;
 +				best_delta = delta;
 +			}
 +			/* fast return in case of "perfect result" */
 +			if (!delta)
 +				return 0;
 +		}
 +	}
 +
 +	return 0;
 +}
 +
 +static void lvds_pll_config(struct stm_lvds *lvds, struct lvds_phy_info *phy)
 +{
 +	unsigned int pll_in_khz, bdiv = 0, mdiv = 0, ndiv = 0;
 +	struct clk_hw *hwclk;
 +	int multiplier;
 +
 +	/*
 +	 * The LVDS PHY includes a low power low jitter high performance and
 +	 * highly configuration Phase Locked Loop supporting integer and
 +	 * fractional multiplication ratios and Spread Spectrum Clocking.  In
 +	 * integer mode, the only software supported feature for now, the PLL is
 +	 * made of a pre-divider NDIV, a feedback multiplier MDIV, followed by
 +	 * several post-dividers, each one with a specific application.
 +	 *
 +	 *          ,------.         ,-----.     ,-----.
 +	 * Fref --> | NDIV | -Fpdf-> | PFD | --> | VCO | --------> Fvco
 +	 *          `------'     ,-> |     |     `-----'  |
 +	 *                       |   `-----'              |
 +	 *                       |         ,------.       |
 +	 *                       `-------- | MDIV | <-----'
 +	 *                                 `------'
 +	 *
 +	 * From the output of the VCO, the clock can be optionally extracted on
 +	 * the RCC clock observer, with a divider TDIV, for testing purpose, or
 +	 * is passed through a programmable post-divider BDIV.  Finally, the
 +	 * frequency can be divided further with two fixed dividers.
 +	 *
 +	 *                            ,--------.
 +	 *                    ,-----> | DP div | ----------------> Fdp
 +	 *          ,------.  |       `--------'
 +	 * Fvco --> | BDIV | ------------------------------------> Fbit
 +	 *      |   `------'    ,------.   |
 +	 *      `-------------> | TDIV | --.---------------------> ClkObs
 +	 *                      '------'   |    ,--------.
 +	 *                                 `--> | LS div | ------> Fls
 +	 *                                      '--------'
 +	 *
 +	 * The LS and DP clock dividers operate at a fixed ratio of 7 and 3.5
 +	 * respectively with regards to fbit. LS divider converts the bit clock
 +	 * to a pixel clock per lane per clock sample (Fls).  This is useful
 +	 * when used to generate a dot clock for the display unit RGB output,
 +	 * and DP divider is.
 +	 */
 +
 +	hwclk = __clk_get_hw(lvds->pllref_clk);
 +	if (!hwclk)
 +		return;
 +
 +	pll_in_khz = clk_hw_get_rate(hwclk) / 1000;
 +
 +	if (lvds_is_dual_link(lvds->link_type))
 +		multiplier = 2;
 +	else
 +		multiplier = 1;
 +
 +	lvds_pll_get_params(lvds, pll_in_khz,
 +			    lvds->pixel_clock_rate * 7 / 1000 / multiplier,
 +			    &bdiv, &mdiv, &ndiv);
 +
 +	/* Set BDIV, MDIV and NDIV */
 +	lvds_write(lvds, phy->base + phy->ofs.PLLCR2, ndiv << 16);
 +	lvds_set(lvds, phy->base + phy->ofs.PLLCR2, bdiv);
 +	lvds_write(lvds, phy->base + phy->ofs.PLLSDCR1, mdiv);
 +
 +	/* Hardcode TDIV as dynamic values are not yet implemented */
 +	lvds_write(lvds, phy->base + phy->ofs.PLLTESTCR, TDIV << 16);
 +
 +	/*
 +	 * For now, PLL just needs to be in integer mode
 +	 * Fractional and spread spectrum clocking are not yet implemented
 +	 *
 +	 * PLL integer mode:
 +	 *	- PMRY_PLL_TWG_STEP = PMRY_PLL_SD_INT_RATIO
 +	 *	- EN_TWG = 0
 +	 *	- EN_SD = 0
 +	 *	- DOWN_SPREAD = 0
 +	 *
 +	 * PLL fractional mode:
 +	 *	- EN_TWG = 0
 +	 *	- EN_SD = 1
 +	 *	- DOWN_SPREAD = 0
 +	 *
 +	 * Spread Spectrum Clocking
 +	 *	- EN_TWG = 1
 +	 *	- EN_SD = 1
 +	 */
 +
 +	/* Disable TWG and SD */
 +	lvds_clear(lvds, phy->base + phy->ofs.PLLCR1, PHY_PLLCR1_EN_TWG | PHY_PLLCR1_EN_SD);
 +
 +	/* Power up bias and PLL dividers */
 +	lvds_set(lvds, phy->base + phy->ofs.DCR, PHY_DCR_POWER_OK);
 +	lvds_set(lvds, phy->base + phy->ofs.CMCR1, PHY_CMCR_CM_EN_DL);
 +	lvds_set(lvds, phy->base + phy->ofs.CMCR2, PHY_CMCR_CM_EN_DL4);
 +
 +	/* Set up voltage mode */
 +	lvds_set(lvds, phy->base + phy->ofs.PLLCPCR, 0x1);
 +	lvds_set(lvds, phy->base + phy->ofs.BCR3, PHY_BCR3_VM_EN_DL);
 +	lvds_set(lvds, phy->base + phy->ofs.BCR1, PHY_BCR1_EN_BIAS_DL);
 +	/* Enable digital datalanes */
 +	lvds_set(lvds, phy->base + phy->ofs.CFGCR, PHY_CFGCR_EN_DIG_DL);
 +}
 +
 +static int lvds_pixel_clk_enable(struct clk_hw *hw)
 +{
 +	struct stm_lvds *lvds = container_of(hw, struct stm_lvds, lvds_ck_px);
 +	struct drm_device *drm = lvds->lvds_bridge.dev;
 +	struct lvds_phy_info *phy;
 +	int ret;
 +
 +	ret = clk_prepare_enable(lvds->pclk);
 +	if (ret) {
 +		drm_err(drm, "Failed to enable lvds peripheral clk\n");
 +		return ret;
 +	}
 +
 +	ret = clk_prepare_enable(lvds->pllref_clk);
 +	if (ret) {
 +		drm_err(drm, "Failed to enable lvds reference clk\n");
 +		clk_disable_unprepare(lvds->pclk);
 +		return ret;
 +	}
 +
 +	/* In case we are operating in dual link the second PHY is set before the primary PHY. */
 +	if (lvds->secondary) {
 +		phy = lvds->secondary;
 +
 +		/* Release LVDS PHY from reset mode */
 +		lvds_set(lvds, phy->base + phy->ofs.GCR, PHY_GCR_DIV_RSTN | PHY_GCR_RSTZ);
 +		lvds_pll_config(lvds, phy);
 +
 +		ret = lvds_pll_enable(lvds, phy);
 +		if (ret) {
 +			drm_err(drm, "Failed to enable secondary PHY PLL: %d\n", ret);
 +			return ret;
 +		}
 +	}
 +
 +	if (lvds->primary) {
 +		phy = lvds->primary;
 +
 +		/* Release LVDS PHY from reset mode */
 +		lvds_set(lvds, phy->base + phy->ofs.GCR, PHY_GCR_DIV_RSTN | PHY_GCR_RSTZ);
 +		lvds_pll_config(lvds, phy);
 +
 +		ret = lvds_pll_enable(lvds, phy);
 +		if (ret) {
 +			drm_err(drm, "Failed to enable primary PHY PLL: %d\n", ret);
 +			return ret;
 +		}
 +	}
 +
 +	return 0;
 +}
 +
 +static void lvds_pixel_clk_disable(struct clk_hw *hw)
 +{
 +	struct stm_lvds *lvds = container_of(hw, struct stm_lvds, lvds_ck_px);
 +
 +	/*
 +	 * For each PHY:
 +	 * Disable DP, LS, BIT clock outputs
 +	 * Shutdown the PLL
 +	 * Assert LVDS PHY in reset mode
 +	 */
 +
 +	if (lvds->primary) {
 +		lvds_clear(lvds, lvds->primary->base + lvds->primary->ofs.GCR,
 +			   (PHY_GCR_DP_CLK_OUT | PHY_GCR_LS_CLK_OUT | PHY_GCR_BIT_CLK_OUT));
 +		lvds_clear(lvds, lvds->primary->base + lvds->primary->ofs.PLLCR1,
 +			   PHY_PLLCR1_PLL_EN);
 +		lvds_clear(lvds, lvds->primary->base + lvds->primary->ofs.GCR,
 +			   PHY_GCR_DIV_RSTN | PHY_GCR_RSTZ);
 +	}
 +
 +	if (lvds->secondary) {
 +		lvds_clear(lvds, lvds->secondary->base + lvds->secondary->ofs.GCR,
 +			   (PHY_GCR_DP_CLK_OUT | PHY_GCR_LS_CLK_OUT | PHY_GCR_BIT_CLK_OUT));
 +		lvds_clear(lvds, lvds->secondary->base + lvds->secondary->ofs.PLLCR1,
 +			   PHY_PLLCR1_PLL_EN);
 +		lvds_clear(lvds, lvds->secondary->base + lvds->secondary->ofs.GCR,
 +			   PHY_GCR_DIV_RSTN | PHY_GCR_RSTZ);
 +	}
 +
 +	clk_disable_unprepare(lvds->pllref_clk);
 +	clk_disable_unprepare(lvds->pclk);
 +}
 +
 +static unsigned long lvds_pixel_clk_recalc_rate(struct clk_hw *hw,
 +						unsigned long parent_rate)
 +{
 +	struct stm_lvds *lvds = container_of(hw, struct stm_lvds, lvds_ck_px);
 +	struct drm_device *drm = lvds->lvds_bridge.dev;
 +	unsigned int pll_in_khz, bdiv, mdiv, ndiv;
 +	int ret, multiplier, pll_out_khz;
 +	u32 val;
 +
 +	ret = clk_prepare_enable(lvds->pclk);
 +	if (ret) {
 +		drm_err(drm, "Failed to enable lvds peripheral clk\n");
 +		return 0;
 +	}
 +
 +	if (lvds_is_dual_link(lvds->link_type))
 +		multiplier = 2;
 +	else
 +		multiplier = 1;
 +
 +	val = lvds_read(lvds, lvds->primary->base + lvds->primary->ofs.PLLCR2);
 +
 +	ndiv = (val & PHY_PLLCR2_NDIV) >> 16;
 +	bdiv = (val & PHY_PLLCR2_BDIV) >> 0;
 +
 +	mdiv = (unsigned int)lvds_read(lvds,
 +				       lvds->primary->base + lvds->primary->ofs.PLLSDCR1);
 +
 +	pll_in_khz = (unsigned int)(parent_rate / 1000);
 +
 +	/* Compute values if not yet accessible */
 +	if (val == 0 || mdiv == 0) {
 +		lvds_pll_get_params(lvds, pll_in_khz,
 +				    lvds->pixel_clock_rate * 7 / 1000 / multiplier,
 +				    &bdiv, &mdiv, &ndiv);
 +	}
 +
 +	pll_out_khz = pll_get_clkout_khz(pll_in_khz, bdiv, mdiv, ndiv);
 +	drm_dbg(drm, "ndiv %d , bdiv %d, mdiv %d, pll_out_khz %d\n",
 +		ndiv, bdiv, mdiv, pll_out_khz);
 +
 +	/*
 +	 * 1/7 because for each pixel in 1 lane there is 7 bits
 +	 * We want pixclk, not bitclk
 +	 */
 +	lvds->pixel_clock_rate = pll_out_khz * 1000 * multiplier / 7;
 +
 +	clk_disable_unprepare(lvds->pclk);
 +
 +	return (unsigned long)lvds->pixel_clock_rate;
 +}
 +
 +static long lvds_pixel_clk_round_rate(struct clk_hw *hw, unsigned long rate,
 +				      unsigned long *parent_rate)
 +{
 +	struct stm_lvds *lvds = container_of(hw, struct stm_lvds, lvds_ck_px);
 +	unsigned int pll_in_khz, bdiv = 0, mdiv = 0, ndiv = 0;
 +	const struct drm_connector *connector;
 +	const struct drm_display_mode *mode;
 +	int multiplier;
 +
 +	connector = &lvds->connector;
 +	if (!connector)
 +		return -EINVAL;
 +
 +	if (list_empty(&connector->modes)) {
 +		drm_dbg(connector->dev, "connector: empty modes list\n");
 +		return -EINVAL;
 +	}
 +
 +	mode = list_first_entry(&connector->modes,
 +				struct drm_display_mode, head);
 +
 +	pll_in_khz = (unsigned int)(*parent_rate / 1000);
 +
 +	if (lvds_is_dual_link(lvds->link_type))
 +		multiplier = 2;
 +	else
 +		multiplier = 1;
 +
 +	lvds_pll_get_params(lvds, pll_in_khz, mode->clock * 7 / multiplier, &bdiv, &mdiv, &ndiv);
 +
 +	/*
 +	 * 1/7 because for each pixel in 1 lane there is 7 bits
 +	 * We want pixclk, not bitclk
 +	 */
 +	lvds->pixel_clock_rate = (unsigned long)pll_get_clkout_khz(pll_in_khz, bdiv, mdiv, ndiv)
 +					 * 1000 * multiplier / 7;
 +
 +	return lvds->pixel_clock_rate;
 +}
 +
 +static const struct clk_ops lvds_pixel_clk_ops = {
 +	.enable = lvds_pixel_clk_enable,
 +	.disable = lvds_pixel_clk_disable,
 +	.recalc_rate = lvds_pixel_clk_recalc_rate,
 +	.round_rate = lvds_pixel_clk_round_rate,
 +};
 +
 +static const struct clk_init_data clk_data = {
 +	.name = "clk_pix_lvds",
 +	.ops = &lvds_pixel_clk_ops,
 +	.parent_names = (const char * []) {"ck_ker_lvdsphy"},
 +	.num_parents = 1,
 +	.flags = CLK_IGNORE_UNUSED,
 +};
 +
 +static void lvds_pixel_clk_unregister(void *data)
 +{
 +	struct stm_lvds *lvds = data;
 +
 +	of_clk_del_provider(lvds->dev->of_node);
 +	clk_hw_unregister(&lvds->lvds_ck_px);
 +}
 +
 +static int lvds_pixel_clk_register(struct stm_lvds *lvds)
 +{
 +	struct device_node *node = lvds->dev->of_node;
 +	int ret;
 +
 +	lvds->lvds_ck_px.init = &clk_data;
 +
 +	/* set the rate by default at 148500000 */
 +	lvds->pixel_clock_rate = 148500000;
 +
 +	ret = clk_hw_register(lvds->dev, &lvds->lvds_ck_px);
 +	if (ret)
 +		return ret;
 +
 +	ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get,
 +				     &lvds->lvds_ck_px);
 +	if (ret)
 +		clk_hw_unregister(&lvds->lvds_ck_px);
 +
 +	return ret;
 +}
 +
 +/*
 + * Host configuration related
 + */
 +static void lvds_config_data_mapping(struct stm_lvds *lvds)
 +{
 +	struct drm_device *drm = lvds->lvds_bridge.dev;
 +	const struct drm_display_info *info;
 +	enum lvds_pixel (*bitmap)[7];
 +	u32 lvds_dmlcr, lvds_dmmcr;
 +	int i;
 +
 +	info = &(&lvds->connector)->display_info;
 +	if (!info->num_bus_formats || !info->bus_formats) {
 +		drm_warn(drm, "No LVDS bus format reported\n");
 +		return;
 +	}
 +
 +	switch (info->bus_formats[0]) {
 +	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: /* VESA-RGB666 */
 +		drm_warn(drm, "Pixel format with data mapping not yet supported.\n");
 +		return;
 +	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: /* VESA-RGB888 */
 +		bitmap = lvds_bitmap_vesa_rgb888;
 +		break;
 +	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: /* JEIDA-RGB888 */
 +		bitmap = lvds_bitmap_jeida_rgb888;
 +		break;
 +	default:
 +		drm_warn(drm, "Unsupported LVDS bus format 0x%04x\n", info->bus_formats[0]);
 +		return;
 +	}
 +
 +	/* Set bitmap for each lane */
 +	for (i = 0; i < 5; i++) {
 +		lvds_dmlcr = ((bitmap[i][0])
 +			      + (bitmap[i][1] << 5)
 +			      + (bitmap[i][2] << 10)
 +			      + (bitmap[i][3] << 15));
 +		lvds_dmmcr = ((bitmap[i][4])
 +			      + (bitmap[i][5] << 5)
 +			      + (bitmap[i][6] << 10));
 +
 +		lvds_write(lvds, LVDS_DMLCR(i), lvds_dmlcr);
 +		lvds_write(lvds, LVDS_DMMCR(i), lvds_dmmcr);
 +	}
 +}
 +
 +static void lvds_config_mode(struct stm_lvds *lvds)
 +{
 +	u32 bus_flags, lvds_cr = 0, lvds_cdl1cr = 0, lvds_cdl2cr = 0;
 +	const struct drm_display_mode *mode;
 +	const struct drm_connector *connector;
 +
 +	connector = &lvds->connector;
 +	if (!connector)
 +		return;
 +
 +	if (list_empty(&connector->modes)) {
 +		drm_dbg(connector->dev, "connector: empty modes list\n");
 +		return;
 +	}
 +
 +	bus_flags = connector->display_info.bus_flags;
 +	mode = list_first_entry(&connector->modes,
 +				struct drm_display_mode, head);
 +
 +	lvds_clear(lvds, LVDS_CR, CR_LKMOD);
 +	lvds_clear(lvds, LVDS_CDL1CR, CDLCR_DISTR0 | CDLCR_DISTR1 | CDLCR_DISTR2 |
 +				      CDLCR_DISTR3 | CDLCR_DISTR4);
 +	lvds_clear(lvds, LVDS_CDL2CR, CDLCR_DISTR0 | CDLCR_DISTR1 | CDLCR_DISTR2 |
 +				      CDLCR_DISTR3 | CDLCR_DISTR4);
 +
 +	/* Set channel distribution */
 +	if (lvds->primary)
 +		lvds_cdl1cr = CDL1CR_DEFAULT;
 +
 +	if (lvds->secondary) {
 +		lvds_cr |= CR_LKMOD;
 +		lvds_cdl2cr = CDL2CR_DEFAULT;
 +	}
 +
 +	/* Set signal polarity */
 +	if (bus_flags & DRM_BUS_FLAG_DE_LOW)
 +		lvds_cr |= CR_DEPOL;
 +
 +	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
 +		lvds_cr |= CR_HSPOL;
 +
 +	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
 +		lvds_cr |= CR_VSPOL;
 +
 +	switch (lvds->link_type) {
 +	case LVDS_DUAL_LINK_EVEN_ODD_PIXELS: /* LKPHA = 0 */
 +		lvds_cr &= ~CR_LKPHA;
 +		break;
 +	case LVDS_DUAL_LINK_ODD_EVEN_PIXELS: /* LKPHA = 1 */
 +		lvds_cr |= CR_LKPHA;
 +		break;
 +	default:
 +		drm_notice(lvds->lvds_bridge.dev, "No phase precised, setting default\n");
 +		lvds_cr &= ~CR_LKPHA;
 +		break;
 +	}
 +
 +	/* Write config to registers */
 +	lvds_set(lvds, LVDS_CR, lvds_cr);
 +	lvds_write(lvds, LVDS_CDL1CR, lvds_cdl1cr);
 +	lvds_write(lvds, LVDS_CDL2CR, lvds_cdl2cr);
 +}
 +
 +static int lvds_connector_get_modes(struct drm_connector *connector)
 +{
 +	struct stm_lvds *lvds = connector_to_stm_lvds(connector);
 +
 +	return drm_panel_get_modes(lvds->panel, connector);
 +}
 +
 +static int lvds_connector_atomic_check(struct drm_connector *connector,
 +				       struct drm_atomic_state *state)
 +{
 +	const struct drm_display_mode *panel_mode;
 +	struct drm_connector_state *conn_state;
 +	struct drm_crtc_state *crtc_state;
 +
 +	conn_state = drm_atomic_get_new_connector_state(state, connector);
 +	if (!conn_state)
 +		return -EINVAL;
 +
 +	if (list_empty(&connector->modes)) {
 +		drm_dbg(connector->dev, "connector: empty modes list\n");
 +		return -EINVAL;
 +	}
 +
 +	if (!conn_state->crtc)
 +		return -EINVAL;
 +
 +	panel_mode = list_first_entry(&connector->modes,
 +				      struct drm_display_mode, head);
 +
 +	/* We're not allowed to modify the resolution. */
 +	crtc_state = drm_atomic_get_crtc_state(state, conn_state->crtc);
 +	if (IS_ERR(crtc_state))
 +		return PTR_ERR(crtc_state);
 +
 +	if (crtc_state->mode.hdisplay != panel_mode->hdisplay ||
 +	    crtc_state->mode.vdisplay != panel_mode->vdisplay)
 +		return -EINVAL;
 +
 +	/* The flat panel mode is fixed, just copy it to the adjusted mode. */
 +	drm_mode_copy(&crtc_state->adjusted_mode, panel_mode);
 +
 +	return 0;
 +}
 +
 +static const struct drm_connector_helper_funcs lvds_conn_helper_funcs = {
 +	.get_modes = lvds_connector_get_modes,
 +	.atomic_check = lvds_connector_atomic_check,
 +};
 +
 +static const struct drm_connector_funcs lvds_conn_funcs = {
 +	.reset = drm_atomic_helper_connector_reset,
 +	.fill_modes = drm_helper_probe_single_connector_modes,
 +	.destroy = drm_connector_cleanup,
 +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 +};
 +
 +static int lvds_attach(struct drm_bridge *bridge,
 +		       enum drm_bridge_attach_flags flags)
 +{
 +	struct stm_lvds *lvds = bridge_to_stm_lvds(bridge);
 +	struct drm_connector *connector = &lvds->connector;
 +	struct drm_encoder *encoder = bridge->encoder;
 +	int ret;
 +
 +	if (!bridge->encoder) {
 +		drm_err(bridge->dev, "Parent encoder object not found\n");
 +		return -ENODEV;
 +	}
 +
 +	/* Set the encoder type as caller does not know it */
 +	bridge->encoder->encoder_type = DRM_MODE_ENCODER_LVDS;
 +
 +	/* No cloning support */
 +	bridge->encoder->possible_clones = 0;
 +
 +	/* If we have a next bridge just attach it. */
 +	if (lvds->next_bridge)
 +		return drm_bridge_attach(bridge->encoder, lvds->next_bridge,
 +					 bridge, flags);
 +
 +	if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
 +		drm_err(bridge->dev, "Fix bridge driver to make connector optional!");
 +		return -EINVAL;
 +	}
 +
 +	/* Otherwise if we have a panel, create a connector. */
 +	if (!lvds->panel)
 +		return 0;
 +
 +	ret = drm_connector_init(bridge->dev, connector,
 +				 &lvds_conn_funcs, DRM_MODE_CONNECTOR_LVDS);
 +	if (ret < 0)
 +		return ret;
 +
 +	drm_connector_helper_add(connector, &lvds_conn_helper_funcs);
 +
 +	ret = drm_connector_attach_encoder(connector, encoder);
 +
 +	return ret;
 +}
 +
 +static void lvds_atomic_enable(struct drm_bridge *bridge,
 +			       struct drm_bridge_state *old_bridge_state)
 +{
 +	struct drm_atomic_state *state = old_bridge_state->base.state;
 +	struct stm_lvds *lvds = bridge_to_stm_lvds(bridge);
 +	struct drm_connector_state *conn_state;
 +	struct drm_connector *connector;
 +	int ret;
 +
 +	ret = clk_prepare_enable(lvds->pclk);
 +	if (ret) {
 +		drm_err(bridge->dev, "Failed to enable lvds peripheral clk\n");
 +		return;
 +	}
 +
 +	connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
 +	if (!connector)
 +		return;
 +
 +	conn_state = drm_atomic_get_new_connector_state(state, connector);
 +	if (!conn_state)
 +		return;
 +
 +	lvds_config_mode(lvds);
 +
 +	/* Set Data Mapping */
 +	lvds_config_data_mapping(lvds);
 +
 +	/* Turn the output on. */
 +	lvds_set(lvds, LVDS_CR, CR_LVDSEN);
 +
 +	if (lvds->panel) {
 +		drm_panel_prepare(lvds->panel);
 +		drm_panel_enable(lvds->panel);
 +	}
 +}
 +
 +static void lvds_atomic_disable(struct drm_bridge *bridge,
 +				struct drm_bridge_state *old_bridge_state)
 +{
 +	struct stm_lvds *lvds = bridge_to_stm_lvds(bridge);
 +
 +	if (lvds->panel) {
 +		drm_panel_disable(lvds->panel);
 +		drm_panel_unprepare(lvds->panel);
 +	}
 +
 +	/* Disable LVDS module */
 +	lvds_clear(lvds, LVDS_CR, CR_LVDSEN);
 +
 +	clk_disable_unprepare(lvds->pclk);
 +}
 +
 +static const struct drm_bridge_funcs lvds_bridge_funcs = {
 +	.attach = lvds_attach,
 +	.atomic_enable = lvds_atomic_enable,
 +	.atomic_disable = lvds_atomic_disable,
 +	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
 +	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
 +	.atomic_reset = drm_atomic_helper_bridge_reset,
 +};
 +
 +static int lvds_probe(struct platform_device *pdev)
 +{
 +	struct device_node *port1, *port2, *remote;
 +	struct device *dev = &pdev->dev;
 +	struct reset_control *rstc;
 +	struct stm_lvds *lvds;
 +	int ret, dual_link;
 +
 +	dev_dbg(dev, "Probing LVDS driver...\n");
 +
 +	lvds = devm_kzalloc(dev, sizeof(*lvds), GFP_KERNEL);
 +	if (!lvds)
 +		return -ENOMEM;
 +
 +	lvds->dev = dev;
 +
 +	ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0,
 +					  &lvds->panel, &lvds->next_bridge);
 +	if (ret) {
 +		dev_err_probe(dev, ret, "Panel not found\n");
 +		return ret;
 +	}
 +
 +	lvds->base = devm_platform_ioremap_resource(pdev, 0);
 +	if (IS_ERR(lvds->base)) {
 +		ret = PTR_ERR(lvds->base);
 +		dev_err(dev, "Unable to get regs %d\n", ret);
 +		return ret;
 +	}
 +
 +	lvds->pclk = devm_clk_get(dev, "pclk");
 +	if (IS_ERR(lvds->pclk)) {
 +		ret = PTR_ERR(lvds->pclk);
 +		dev_err(dev, "Unable to get peripheral clock: %d\n", ret);
 +		return ret;
 +	}
 +
 +	ret = clk_prepare_enable(lvds->pclk);
 +	if (ret) {
 +		dev_err(dev, "%s: Failed to enable peripheral clk\n", __func__);
 +		return ret;
 +	}
 +
 +	rstc = devm_reset_control_get_exclusive(dev, NULL);
 +
 +	if (IS_ERR(rstc)) {
 +		ret = PTR_ERR(rstc);
 +		goto err_lvds_probe;
 +	}
 +
 +	reset_control_assert(rstc);
 +	usleep_range(10, 20);
 +	reset_control_deassert(rstc);
 +
 +	port1 = of_graph_get_port_by_id(dev->of_node, 1);
 +	port2 = of_graph_get_port_by_id(dev->of_node, 2);
 +	dual_link = drm_of_lvds_get_dual_link_pixel_order(port1, port2);
 +
 +	switch (dual_link) {
 +	case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS:
 +		lvds->link_type = LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
 +		lvds->primary = &lvds_phy_16ff_primary;
 +		lvds->secondary = &lvds_phy_16ff_secondary;
 +		break;
 +	case DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS:
 +		lvds->link_type = LVDS_DUAL_LINK_EVEN_ODD_PIXELS;
 +		lvds->primary = &lvds_phy_16ff_primary;
 +		lvds->secondary = &lvds_phy_16ff_secondary;
 +		break;
 +	case -EINVAL:
 +		/*
 +		 * drm_of_lvds_get_dual_pixel_order returns 4 possible values.
 +		 * In the case where the returned value is an error, it can be
 +		 * either ENODEV or EINVAL. Seeing the structure of this
 +		 * function, EINVAL means that either port1 or port2 is not
 +		 * present in the device tree.
 +		 * In that case, the lvds panel can be a single link panel, or
 +		 * there is a semantical error in the device tree code.
 +		 */
 +		remote = of_get_next_available_child(port1, NULL);
 +		if (remote) {
 +			if (of_graph_get_remote_endpoint(remote)) {
 +				lvds->link_type = LVDS_SINGLE_LINK_PRIMARY;
 +				lvds->primary = &lvds_phy_16ff_primary;
 +				lvds->secondary = NULL;
 +			} else {
 +				ret = -EINVAL;
 +			}
 +
 +			of_node_put(remote);
 +		}
 +
 +		remote = of_get_next_available_child(port2, NULL);
 +		if (remote) {
 +			if (of_graph_get_remote_endpoint(remote)) {
 +				lvds->link_type = LVDS_SINGLE_LINK_SECONDARY;
 +				lvds->primary = NULL;
 +				lvds->secondary = &lvds_phy_16ff_secondary;
 +			} else {
 +				ret = (ret == -EINVAL) ? -EINVAL : 0;
 +			}
 +
 +			of_node_put(remote);
 +		}
 +		break;
 +	default:
 +		ret = -EINVAL;
 +		goto err_lvds_probe;
 +	}
 +	of_node_put(port1);
 +	of_node_put(port2);
 +
 +	lvds->pllref_clk = devm_clk_get(dev, "ref");
 +	if (IS_ERR(lvds->pllref_clk)) {
 +		ret = PTR_ERR(lvds->pllref_clk);
 +		dev_err(dev, "Unable to get reference clock: %d\n", ret);
 +		goto err_lvds_probe;
 +	}
 +
 +	ret = lvds_pixel_clk_register(lvds);
 +	if (ret) {
 +		dev_err(dev, "Failed to register LVDS pixel clock: %d\n", ret);
 +		goto err_lvds_probe;
 +	}
 +
 +	lvds->lvds_bridge.funcs = &lvds_bridge_funcs;
 +	lvds->lvds_bridge.of_node = dev->of_node;
 +	lvds->hw_version = lvds_read(lvds, LVDS_VERR);
 +
 +	dev_info(dev, "version 0x%02x initialized\n", lvds->hw_version);
 +
 +	drm_bridge_add(&lvds->lvds_bridge);
 +
 +	platform_set_drvdata(pdev, lvds);
 +
 +	clk_disable_unprepare(lvds->pclk);
 +
 +	return 0;
 +
 +err_lvds_probe:
 +	clk_disable_unprepare(lvds->pclk);
 +
 +	return ret;
 +}
 +
- static int lvds_remove(struct platform_device *pdev)
++static void lvds_remove(struct platform_device *pdev)
 +{
 +	struct stm_lvds *lvds = platform_get_drvdata(pdev);
 +
 +	lvds_pixel_clk_unregister(lvds);
 +
 +	drm_bridge_remove(&lvds->lvds_bridge);
-
- 	return 0;
 +}
 +
 +static const struct of_device_id lvds_dt_ids[] = {
 +	{
 +		.compatible = "st,stm32mp25-lvds",
 +		.data = NULL
 +	},
 +	{ /* sentinel */ }
 +};
 +
 +MODULE_DEVICE_TABLE(of, lvds_dt_ids);
 +
 +static struct platform_driver lvds_platform_driver = {
 +	.probe = lvds_probe,
 +	.remove = lvds_remove,
 +	.driver = {
 +		.name = "stm32-display-lvds",
 +		.owner = THIS_MODULE,
 +		.of_match_table = lvds_dt_ids,
 +	},
 +};
 +
 +module_platform_driver(lvds_platform_driver);
 +
 +MODULE_AUTHOR("Raphaël Gallais-Pou <raphael.gallais-pou@foss.st.com>");
 +MODULE_AUTHOR("Philippe Cornu <philippe.cornu@foss.st.com>");
 +MODULE_AUTHOR("Yannick Fertre <yannick.fertre@foss.st.com>");
 +MODULE_DESCRIPTION("STMicroelectronics LVDS Display Interface Transmitter DRM driver");
 +MODULE_LICENSE("GPL");
diff --cc drivers/net/ethernet/renesas/rtsn.c
index 577227c007ab,000000000000..0e6cea42f007
mode 100644,000000..100644
--- a/drivers/net/ethernet/renesas/rtsn.c
+++ b/drivers/net/ethernet/renesas/rtsn.c
@@@ -1,1391 -1,0 +1,1389 @@@
 +// SPDX-License-Identifier: GPL-2.0
 +
 +/* Renesas Ethernet-TSN device driver
 + *
 + * Copyright (C) 2022 Renesas Electronics Corporation
 + * Copyright (C) 2023 Niklas Söderlund <niklas.soderlund@ragnatech.se>
 + */
 +
 +#include <linux/clk.h>
 +#include <linux/dma-mapping.h>
 +#include <linux/etherdevice.h>
 +#include <linux/ethtool.h>
 +#include <linux/module.h>
 +#include <linux/net_tstamp.h>
 +#include <linux/of.h>
 +#include <linux/of_mdio.h>
 +#include <linux/of_net.h>
 +#include <linux/phy.h>
 +#include <linux/platform_device.h>
 +#include <linux/pm_runtime.h>
 +#include <linux/reset.h>
 +#include <linux/spinlock.h>
 +
 +#include "rtsn.h"
 +#include "rcar_gen4_ptp.h"
 +
 +struct rtsn_private {
 +	struct net_device *ndev;
 +	struct platform_device *pdev;
 +	void __iomem *base;
 +	struct rcar_gen4_ptp_private *ptp_priv;
 +	struct clk *clk;
 +	struct reset_control *reset;
 +
 +	u32 num_tx_ring;
 +	u32 num_rx_ring;
 +	u32 tx_desc_bat_size;
 +	dma_addr_t tx_desc_bat_dma;
 +	struct rtsn_desc *tx_desc_bat;
 +	u32 rx_desc_bat_size;
 +	dma_addr_t rx_desc_bat_dma;
 +	struct rtsn_desc *rx_desc_bat;
 +	dma_addr_t tx_desc_dma;
 +	dma_addr_t rx_desc_dma;
 +	struct rtsn_ext_desc *tx_ring;
 +	struct rtsn_ext_ts_desc *rx_ring;
 +	struct sk_buff **tx_skb;
 +	struct sk_buff **rx_skb;
 +	spinlock_t lock;	/* Register access lock */
 +	u32 cur_tx;
 +	u32 dirty_tx;
 +	u32 cur_rx;
 +	u32 dirty_rx;
 +	u8 ts_tag;
 +	struct napi_struct napi;
 +	struct rtnl_link_stats64 stats;
 +
 +	struct mii_bus *mii;
 +	phy_interface_t iface;
 +	int link;
 +	int speed;
 +
 +	int tx_data_irq;
 +	int rx_data_irq;
 +};
 +
 +static u32 rtsn_read(struct rtsn_private *priv, enum rtsn_reg reg)
 +{
 +	return ioread32(priv->base + reg);
 +}
 +
 +static void rtsn_write(struct rtsn_private *priv, enum rtsn_reg reg, u32 data)
 +{
 +	iowrite32(data, priv->base + reg);
 +}
 +
 +static void rtsn_modify(struct rtsn_private *priv, enum rtsn_reg reg,
 +			u32 clear, u32 set)
 +{
 +	rtsn_write(priv, reg, (rtsn_read(priv, reg) & ~clear) | set);
 +}
 +
 +static int rtsn_reg_wait(struct rtsn_private *priv, enum rtsn_reg reg,
 +			 u32 mask, u32 expected)
 +{
 +	u32 val;
 +
 +	return readl_poll_timeout(priv->base + reg, val,
 +				  (val & mask) == expected,
 +				  RTSN_INTERVAL_US, RTSN_TIMEOUT_US);
 +}
 +
 +static void rtsn_ctrl_data_irq(struct rtsn_private *priv, bool enable)
 +{
 +	if (enable) {
 +		rtsn_write(priv, TDIE0, TDIE_TDID_TDX(TX_CHAIN_IDX));
 +		rtsn_write(priv, RDIE0, RDIE_RDID_RDX(RX_CHAIN_IDX));
 +	} else {
 +		rtsn_write(priv, TDID0, TDIE_TDID_TDX(TX_CHAIN_IDX));
 +		rtsn_write(priv, RDID0, RDIE_RDID_RDX(RX_CHAIN_IDX));
 +	}
 +}
 +
 +static void rtsn_get_timestamp(struct rtsn_private *priv, struct timespec64 *ts)
 +{
 +	struct rcar_gen4_ptp_private *ptp_priv = priv->ptp_priv;
 +
 +	ptp_priv->info.gettime64(&ptp_priv->info, ts);
 +}
 +
 +static int rtsn_tx_free(struct net_device *ndev, bool free_txed_only)
 +{
 +	struct rtsn_private *priv = netdev_priv(ndev);
 +	struct rtsn_ext_desc *desc;
 +	struct sk_buff *skb;
 +	int free_num = 0;
 +	int entry, size;
 +
 +	for (; priv->cur_tx - priv->dirty_tx > 0; priv->dirty_tx++) {
 +		entry = priv->dirty_tx % priv->num_tx_ring;
 +		desc = &priv->tx_ring[entry];
 +		if (free_txed_only && (desc->die_dt & DT_MASK) != DT_FEMPTY)
 +			break;
 +
 +		dma_rmb();
 +		size = le16_to_cpu(desc->info_ds) & TX_DS;
 +		skb = priv->tx_skb[entry];
 +		if (skb) {
 +			if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) {
 +				struct skb_shared_hwtstamps shhwtstamps;
 +				struct timespec64 ts;
 +
 +				rtsn_get_timestamp(priv, &ts);
 +				memset(&shhwtstamps, 0, sizeof(shhwtstamps));
 +				shhwtstamps.hwtstamp = timespec64_to_ktime(ts);
 +				skb_tstamp_tx(skb, &shhwtstamps);
 +			}
 +			dma_unmap_single(ndev->dev.parent,
 +					 le32_to_cpu(desc->dptr),
 +					 size, DMA_TO_DEVICE);
 +			dev_kfree_skb_any(priv->tx_skb[entry]);
 +			free_num++;
 +
 +			priv->stats.tx_packets++;
 +			priv->stats.tx_bytes += size;
 +		}
 +
 +		desc->die_dt = DT_EEMPTY;
 +	}
 +
 +	desc = &priv->tx_ring[priv->num_tx_ring];
 +	desc->die_dt = DT_LINK;
 +
 +	return free_num;
 +}
 +
 +static int rtsn_rx(struct net_device *ndev, int budget)
 +{
 +	struct rtsn_private *priv = netdev_priv(ndev);
 +	unsigned int ndescriptors;
 +	unsigned int rx_packets;
 +	unsigned int i;
 +	bool get_ts;
 +
 +	get_ts = priv->ptp_priv->tstamp_rx_ctrl &
 +		RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT;
 +
 +	ndescriptors = priv->dirty_rx + priv->num_rx_ring - priv->cur_rx;
 +	rx_packets = 0;
 +	for (i = 0; i < ndescriptors; i++) {
 +		const unsigned int entry = priv->cur_rx % priv->num_rx_ring;
 +		struct rtsn_ext_ts_desc *desc = &priv->rx_ring[entry];
 +		struct sk_buff *skb;
 +		dma_addr_t dma_addr;
 +		u16 pkt_len;
 +
 +		/* Stop processing descriptors if budget is consumed. */
 +		if (rx_packets >= budget)
 +			break;
 +
 +		/* Stop processing descriptors on first empty. */
 +		if ((desc->die_dt & DT_MASK) == DT_FEMPTY)
 +			break;
 +
 +		dma_rmb();
 +		pkt_len = le16_to_cpu(desc->info_ds) & RX_DS;
 +
 +		skb = priv->rx_skb[entry];
 +		priv->rx_skb[entry] = NULL;
 +		dma_addr = le32_to_cpu(desc->dptr);
 +		dma_unmap_single(ndev->dev.parent, dma_addr, PKT_BUF_SZ,
 +				 DMA_FROM_DEVICE);
 +
 +		/* Get timestamp if enabled. */
 +		if (get_ts) {
 +			struct skb_shared_hwtstamps *shhwtstamps;
 +			struct timespec64 ts;
 +
 +			shhwtstamps = skb_hwtstamps(skb);
 +			memset(shhwtstamps, 0, sizeof(*shhwtstamps));
 +
 +			ts.tv_sec = (u64)le32_to_cpu(desc->ts_sec);
 +			ts.tv_nsec = le32_to_cpu(desc->ts_nsec & cpu_to_le32(0x3fffffff));
 +
 +			shhwtstamps->hwtstamp = timespec64_to_ktime(ts);
 +		}
 +
 +		skb_put(skb, pkt_len);
 +		skb->protocol = eth_type_trans(skb, ndev);
 +		napi_gro_receive(&priv->napi, skb);
 +
 +		/* Update statistics. */
 +		priv->stats.rx_packets++;
 +		priv->stats.rx_bytes += pkt_len;
 +
 +		/* Update counters. */
 +		priv->cur_rx++;
 +		rx_packets++;
 +	}
 +
 +	/* Refill the RX ring buffers */
 +	for (; priv->cur_rx - priv->dirty_rx > 0; priv->dirty_rx++) {
 +		const unsigned int entry = priv->dirty_rx % priv->num_rx_ring;
 +		struct rtsn_ext_ts_desc *desc = &priv->rx_ring[entry];
 +		struct sk_buff *skb;
 +		dma_addr_t dma_addr;
 +
 +		desc->info_ds = cpu_to_le16(PKT_BUF_SZ);
 +
 +		if (!priv->rx_skb[entry]) {
 +			skb = napi_alloc_skb(&priv->napi,
 +					     PKT_BUF_SZ + RTSN_ALIGN - 1);
 +			if (!skb)
 +				break;
 +			skb_reserve(skb, NET_IP_ALIGN);
 +			dma_addr = dma_map_single(ndev->dev.parent, skb->data,
 +						  le16_to_cpu(desc->info_ds),
 +						  DMA_FROM_DEVICE);
 +			if (dma_mapping_error(ndev->dev.parent, dma_addr))
 +				desc->info_ds = cpu_to_le16(0);
 +			desc->dptr = cpu_to_le32(dma_addr);
 +			skb_checksum_none_assert(skb);
 +			priv->rx_skb[entry] = skb;
 +		}
 +
 +		dma_wmb();
 +		desc->die_dt = DT_FEMPTY | D_DIE;
 +	}
 +
 +	priv->rx_ring[priv->num_rx_ring].die_dt = DT_LINK;
 +
 +	return rx_packets;
 +}
 +
 +static int rtsn_poll(struct napi_struct *napi, int budget)
 +{
 +	struct rtsn_private *priv;
 +	struct net_device *ndev;
 +	unsigned long flags;
 +	int work_done;
 +
 +	ndev = napi->dev;
 +	priv = netdev_priv(ndev);
 +
 +	/* Processing RX Descriptor Ring */
 +	work_done = rtsn_rx(ndev, budget);
 +
 +	/* Processing TX Descriptor Ring */
 +	spin_lock_irqsave(&priv->lock, flags);
 +	rtsn_tx_free(ndev, true);
 +	netif_wake_subqueue(ndev, 0);
 +	spin_unlock_irqrestore(&priv->lock, flags);
 +
 +	/* Re-enable TX/RX interrupts */
 +	if (work_done < budget && napi_complete_done(napi, work_done)) {
 +		spin_lock_irqsave(&priv->lock, flags);
 +		rtsn_ctrl_data_irq(priv, true);
 +		spin_unlock_irqrestore(&priv->lock, flags);
 +	}
 +
 +	return work_done;
 +}
 +
 +static int rtsn_desc_alloc(struct rtsn_private *priv)
 +{
 +	struct device *dev = &priv->pdev->dev;
 +	unsigned int i;
 +
 +	priv->tx_desc_bat_size = sizeof(struct rtsn_desc) * TX_NUM_CHAINS;
 +	priv->tx_desc_bat = dma_alloc_coherent(dev, priv->tx_desc_bat_size,
 +					       &priv->tx_desc_bat_dma,
 +					       GFP_KERNEL);
 +
 +	if (!priv->tx_desc_bat)
 +		return -ENOMEM;
 +
 +	for (i = 0; i < TX_NUM_CHAINS; i++)
 +		priv->tx_desc_bat[i].die_dt = DT_EOS;
 +
 +	priv->rx_desc_bat_size = sizeof(struct rtsn_desc) * RX_NUM_CHAINS;
 +	priv->rx_desc_bat = dma_alloc_coherent(dev, priv->rx_desc_bat_size,
 +					       &priv->rx_desc_bat_dma,
 +					       GFP_KERNEL);
 +
 +	if (!priv->rx_desc_bat)
 +		return -ENOMEM;
 +
 +	for (i = 0; i < RX_NUM_CHAINS; i++)
 +		priv->rx_desc_bat[i].die_dt = DT_EOS;
 +
 +	return 0;
 +}
 +
 +static void rtsn_desc_free(struct rtsn_private *priv)
 +{
 +	if (priv->tx_desc_bat)
 +		dma_free_coherent(&priv->pdev->dev, priv->tx_desc_bat_size,
 +				  priv->tx_desc_bat, priv->tx_desc_bat_dma);
 +	priv->tx_desc_bat = NULL;
 +
 +	if (priv->rx_desc_bat)
 +		dma_free_coherent(&priv->pdev->dev, priv->rx_desc_bat_size,
 +				  priv->rx_desc_bat, priv->rx_desc_bat_dma);
 +	priv->rx_desc_bat = NULL;
 +}
 +
 +static void rtsn_chain_free(struct rtsn_private *priv)
 +{
 +	struct device *dev = &priv->pdev->dev;
 +
 +	dma_free_coherent(dev,
 +			  sizeof(struct rtsn_ext_desc) * (priv->num_tx_ring + 1),
 +			  priv->tx_ring, priv->tx_desc_dma);
 +	priv->tx_ring = NULL;
 +
 +	dma_free_coherent(dev,
 +			  sizeof(struct rtsn_ext_ts_desc) * (priv->num_rx_ring + 1),
 +			  priv->rx_ring, priv->rx_desc_dma);
 +	priv->rx_ring = NULL;
 +
 +	kfree(priv->tx_skb);
 +	priv->tx_skb = NULL;
 +
 +	kfree(priv->rx_skb);
 +	priv->rx_skb = NULL;
 +}
 +
 +static int rtsn_chain_init(struct rtsn_private *priv, int tx_size, int rx_size)
 +{
 +	struct net_device *ndev = priv->ndev;
 +	struct sk_buff *skb;
 +	int i;
 +
 +	priv->num_tx_ring = tx_size;
 +	priv->num_rx_ring = rx_size;
 +
 +	priv->tx_skb = kcalloc(tx_size, sizeof(*priv->tx_skb), GFP_KERNEL);
 +	priv->rx_skb = kcalloc(rx_size, sizeof(*priv->rx_skb), GFP_KERNEL);
 +
 +	if (!priv->rx_skb || !priv->tx_skb)
 +		goto error;
 +
 +	for (i = 0; i < rx_size; i++) {
 +		skb = netdev_alloc_skb(ndev, PKT_BUF_SZ + RTSN_ALIGN - 1);
 +		if (!skb)
 +			goto error;
 +		skb_reserve(skb, NET_IP_ALIGN);
 +		priv->rx_skb[i] = skb;
 +	}
 +
 +	/* Allocate TX, RX descriptors */
 +	priv->tx_ring = dma_alloc_coherent(ndev->dev.parent,
 +					   sizeof(struct rtsn_ext_desc) * (tx_size + 1),
 +					   &priv->tx_desc_dma, GFP_KERNEL);
 +	priv->rx_ring = dma_alloc_coherent(ndev->dev.parent,
 +					   sizeof(struct rtsn_ext_ts_desc) * (rx_size + 1),
 +					   &priv->rx_desc_dma, GFP_KERNEL);
 +
 +	if (!priv->tx_ring || !priv->rx_ring)
 +		goto error;
 +
 +	return 0;
 +error:
 +	rtsn_chain_free(priv);
 +
 +	return -ENOMEM;
 +}
 +
 +static void rtsn_chain_format(struct rtsn_private *priv)
 +{
 +	struct net_device *ndev = priv->ndev;
 +	struct rtsn_ext_ts_desc *rx_desc;
 +	struct rtsn_ext_desc *tx_desc;
 +	struct rtsn_desc *bat_desc;
 +	dma_addr_t dma_addr;
 +	unsigned int i;
 +
 +	priv->cur_tx = 0;
 +	priv->cur_rx = 0;
 +	priv->dirty_rx = 0;
 +	priv->dirty_tx = 0;
 +
 +	/* TX */
 +	memset(priv->tx_ring, 0, sizeof(*tx_desc) * priv->num_tx_ring);
 +	for (i = 0, tx_desc = priv->tx_ring; i < priv->num_tx_ring; i++, tx_desc++)
 +		tx_desc->die_dt = DT_EEMPTY | D_DIE;
 +
 +	tx_desc->dptr = cpu_to_le32((u32)priv->tx_desc_dma);
 +	tx_desc->die_dt = DT_LINK;
 +
 +	bat_desc = &priv->tx_desc_bat[TX_CHAIN_IDX];
 +	bat_desc->die_dt = DT_LINK;
 +	bat_desc->dptr = cpu_to_le32((u32)priv->tx_desc_dma);
 +
 +	/* RX */
 +	memset(priv->rx_ring, 0, sizeof(*rx_desc) * priv->num_rx_ring);
 +	for (i = 0, rx_desc = priv->rx_ring; i < priv->num_rx_ring; i++, rx_desc++) {
 +		dma_addr = dma_map_single(ndev->dev.parent,
 +					  priv->rx_skb[i]->data, PKT_BUF_SZ,
 +					  DMA_FROM_DEVICE);
 +		if (!dma_mapping_error(ndev->dev.parent, dma_addr))
 +			rx_desc->info_ds = cpu_to_le16(PKT_BUF_SZ);
 +		rx_desc->dptr = cpu_to_le32((u32)dma_addr);
 +		rx_desc->die_dt = DT_FEMPTY | D_DIE;
 +	}
 +	rx_desc->dptr = cpu_to_le32((u32)priv->rx_desc_dma);
 +	rx_desc->die_dt = DT_LINK;
 +
 +	bat_desc = &priv->rx_desc_bat[RX_CHAIN_IDX];
 +	bat_desc->die_dt = DT_LINK;
 +	bat_desc->dptr = cpu_to_le32((u32)priv->rx_desc_dma);
 +}
 +
 +static int rtsn_dmac_init(struct rtsn_private *priv)
 +{
 +	int ret;
 +
 +	ret = rtsn_chain_init(priv, TX_CHAIN_SIZE, RX_CHAIN_SIZE);
 +	if (ret)
 +		return ret;
 +
 +	rtsn_chain_format(priv);
 +
 +	return 0;
 +}
 +
 +static enum rtsn_mode rtsn_read_mode(struct rtsn_private *priv)
 +{
 +	return (rtsn_read(priv, OSR) & OSR_OPS) >> 1;
 +}
 +
 +static int rtsn_wait_mode(struct rtsn_private *priv, enum rtsn_mode mode)
 +{
 +	unsigned int i;
 +
 +	/* Need to busy loop as mode changes can happen in atomic context. */
 +	for (i = 0; i < RTSN_TIMEOUT_US / RTSN_INTERVAL_US; i++) {
 +		if (rtsn_read_mode(priv) == mode)
 +			return 0;
 +
 +		udelay(RTSN_INTERVAL_US);
 +	}
 +
 +	return -ETIMEDOUT;
 +}
 +
 +static int rtsn_change_mode(struct rtsn_private *priv, enum rtsn_mode mode)
 +{
 +	int ret;
 +
 +	rtsn_write(priv, OCR, mode);
 +	ret = rtsn_wait_mode(priv, mode);
 +	if (ret)
 +		netdev_err(priv->ndev, "Failed to switch operation mode\n");
 +	return ret;
 +}
 +
 +static int rtsn_get_data_irq_status(struct rtsn_private *priv)
 +{
 +	u32 val;
 +
 +	val = rtsn_read(priv, TDIS0) | TDIS_TDS(TX_CHAIN_IDX);
 +	val |= rtsn_read(priv, RDIS0) | RDIS_RDS(RX_CHAIN_IDX);
 +
 +	return val;
 +}
 +
 +static irqreturn_t rtsn_irq(int irq, void *dev_id)
 +{
 +	struct rtsn_private *priv = dev_id;
 +	int ret = IRQ_NONE;
 +
 +	spin_lock(&priv->lock);
 +
 +	if (rtsn_get_data_irq_status(priv)) {
 +		/* Clear TX/RX irq status */
 +		rtsn_write(priv, TDIS0, TDIS_TDS(TX_CHAIN_IDX));
 +		rtsn_write(priv, RDIS0, RDIS_RDS(RX_CHAIN_IDX));
 +
 +		if (napi_schedule_prep(&priv->napi)) {
 +			/* Disable TX/RX interrupts */
 +			rtsn_ctrl_data_irq(priv, false);
 +
 +			__napi_schedule(&priv->napi);
 +		}
 +
 +		ret = IRQ_HANDLED;
 +	}
 +
 +	spin_unlock(&priv->lock);
 +
 +	return ret;
 +}
 +
 +static int rtsn_request_irq(unsigned int irq, irq_handler_t handler,
 +			    unsigned long flags, struct rtsn_private *priv,
 +			    const char *ch)
 +{
 +	char *name;
 +	int ret;
 +
 +	name = devm_kasprintf(&priv->pdev->dev, GFP_KERNEL, "%s:%s",
 +			      priv->ndev->name, ch);
 +	if (!name)
 +		return -ENOMEM;
 +
 +	ret = request_irq(irq, handler, flags, name, priv);
 +	if (ret)
 +		netdev_err(priv->ndev, "Cannot request IRQ %s\n", name);
 +
 +	return ret;
 +}
 +
 +static void rtsn_free_irqs(struct rtsn_private *priv)
 +{
 +	free_irq(priv->tx_data_irq, priv);
 +	free_irq(priv->rx_data_irq, priv);
 +}
 +
 +static int rtsn_request_irqs(struct rtsn_private *priv)
 +{
 +	int ret;
 +
 +	priv->rx_data_irq = platform_get_irq_byname(priv->pdev, "rx");
 +	if (priv->rx_data_irq < 0)
 +		return priv->rx_data_irq;
 +
 +	priv->tx_data_irq = platform_get_irq_byname(priv->pdev, "tx");
 +	if (priv->tx_data_irq < 0)
 +		return priv->tx_data_irq;
 +
 +	ret = rtsn_request_irq(priv->tx_data_irq, rtsn_irq, 0, priv, "tx");
 +	if (ret)
 +		return ret;
 +
 +	ret = rtsn_request_irq(priv->rx_data_irq, rtsn_irq, 0, priv, "rx");
 +	if (ret) {
 +		free_irq(priv->tx_data_irq, priv);
 +		return ret;
 +	}
 +
 +	return 0;
 +}
 +
 +static int rtsn_reset(struct rtsn_private *priv)
 +{
 +	reset_control_reset(priv->reset);
 +	mdelay(1);
 +
 +	return rtsn_wait_mode(priv, OCR_OPC_DISABLE);
 +}
 +
 +static int rtsn_axibmi_init(struct rtsn_private *priv)
 +{
 +	int ret;
 +
 +	ret = rtsn_reg_wait(priv, RR, RR_RST, RR_RST_COMPLETE);
 +	if (ret)
 +		return ret;
 +
 +	/* Set AXIWC */
 +	rtsn_write(priv, AXIWC, AXIWC_DEFAULT);
 +
 +	/* Set AXIRC */
 +	rtsn_write(priv, AXIRC, AXIRC_DEFAULT);
 +
 +	/* TX Descriptor chain setting */
 +	rtsn_write(priv, TATLS0, TATLS0_TEDE | TATLS0_TATEN(TX_CHAIN_IDX));
 +	rtsn_write(priv, TATLS1, priv->tx_desc_bat_dma + TX_CHAIN_ADDR_OFFSET);
 +	rtsn_write(priv, TATLR, TATLR_TATL);
 +
 +	ret = rtsn_reg_wait(priv, TATLR, TATLR_TATL, 0);
 +	if (ret)
 +		return ret;
 +
 +	/* RX Descriptor chain setting */
 +	rtsn_write(priv, RATLS0,
 +		   RATLS0_RETS | RATLS0_REDE | RATLS0_RATEN(RX_CHAIN_IDX));
 +	rtsn_write(priv, RATLS1, priv->rx_desc_bat_dma + RX_CHAIN_ADDR_OFFSET);
 +	rtsn_write(priv, RATLR, RATLR_RATL);
 +
 +	ret = rtsn_reg_wait(priv, RATLR, RATLR_RATL, 0);
 +	if (ret)
 +		return ret;
 +
 +	/* Enable TX/RX interrupts */
 +	rtsn_ctrl_data_irq(priv, true);
 +
 +	return 0;
 +}
 +
 +static void rtsn_mhd_init(struct rtsn_private *priv)
 +{
 +	/* TX General setting */
 +	rtsn_write(priv, TGC1, TGC1_STTV_DEFAULT | TGC1_TQTM_SFM);
 +	rtsn_write(priv, TMS0, TMS_MFS_MAX);
 +
 +	/* RX Filter IP */
 +	rtsn_write(priv, CFCR0, CFCR_SDID(RX_CHAIN_IDX));
 +	rtsn_write(priv, FMSCR, FMSCR_FMSIE(RX_CHAIN_IDX));
 +}
 +
 +static int rtsn_get_phy_params(struct rtsn_private *priv)
 +{
 +	int ret;
 +
 +	ret = of_get_phy_mode(priv->pdev->dev.of_node, &priv->iface);
 +	if (ret)
 +		return ret;
 +
 +	switch (priv->iface) {
 +	case PHY_INTERFACE_MODE_MII:
 +		priv->speed = 100;
 +		break;
 +	case PHY_INTERFACE_MODE_RGMII:
 +	case PHY_INTERFACE_MODE_RGMII_ID:
 +	case PHY_INTERFACE_MODE_RGMII_RXID:
 +	case PHY_INTERFACE_MODE_RGMII_TXID:
 +		priv->speed = 1000;
 +		break;
 +	default:
 +		return -EOPNOTSUPP;
 +	}
 +
 +	return 0;
 +}
 +
 +static void rtsn_set_phy_interface(struct rtsn_private *priv)
 +{
 +	u32 val;
 +
 +	switch (priv->iface) {
 +	case PHY_INTERFACE_MODE_MII:
 +		val = MPIC_PIS_MII;
 +		break;
 +	case PHY_INTERFACE_MODE_RGMII:
 +	case PHY_INTERFACE_MODE_RGMII_ID:
 +	case PHY_INTERFACE_MODE_RGMII_RXID:
 +	case PHY_INTERFACE_MODE_RGMII_TXID:
 +		val = MPIC_PIS_GMII;
 +		break;
 +	default:
 +		return;
 +	}
 +
 +	rtsn_modify(priv, MPIC, MPIC_PIS_MASK, val);
 +}
 +
 +static void rtsn_set_rate(struct rtsn_private *priv)
 +{
 +	u32 val;
 +
 +	switch (priv->speed) {
 +	case 10:
 +		val = MPIC_LSC_10M;
 +		break;
 +	case 100:
 +		val = MPIC_LSC_100M;
 +		break;
 +	case 1000:
 +		val = MPIC_LSC_1G;
 +		break;
 +	default:
 +		return;
 +	}
 +
 +	rtsn_modify(priv, MPIC, MPIC_LSC_MASK, val);
 +}
 +
 +static int rtsn_rmac_init(struct rtsn_private *priv)
 +{
 +	const u8 *mac_addr = priv->ndev->dev_addr;
 +	int ret;
 +
 +	/* Set MAC address */
 +	rtsn_write(priv, MRMAC0, (mac_addr[0] << 8) | mac_addr[1]);
 +	rtsn_write(priv, MRMAC1, (mac_addr[2] << 24) | (mac_addr[3] << 16) |
 +		   (mac_addr[4] << 8) | mac_addr[5]);
 +
 +	/* Set xMII type */
 +	rtsn_set_phy_interface(priv);
 +	rtsn_set_rate(priv);
 +
 +	/* Enable MII */
 +	rtsn_modify(priv, MPIC, MPIC_PSMCS_MASK | MPIC_PSMHT_MASK,
 +		    MPIC_PSMCS_DEFAULT | MPIC_PSMHT_DEFAULT);
 +
 +	/* Link verification */
 +	rtsn_modify(priv, MLVC, MLVC_PLV, MLVC_PLV);
 +	ret = rtsn_reg_wait(priv, MLVC, MLVC_PLV, 0);
 +	if (ret)
 +		return ret;
 +
 +	return ret;
 +}
 +
 +static int rtsn_hw_init(struct rtsn_private *priv)
 +{
 +	int ret;
 +
 +	ret = rtsn_reset(priv);
 +	if (ret)
 +		return ret;
 +
 +	/* Change to CONFIG mode */
 +	ret = rtsn_change_mode(priv, OCR_OPC_CONFIG);
 +	if (ret)
 +		return ret;
 +
 +	ret = rtsn_axibmi_init(priv);
 +	if (ret)
 +		return ret;
 +
 +	rtsn_mhd_init(priv);
 +
 +	ret = rtsn_rmac_init(priv);
 +	if (ret)
 +		return ret;
 +
 +	ret = rtsn_change_mode(priv, OCR_OPC_DISABLE);
 +	if (ret)
 +		return ret;
 +
 +	/* Change to OPERATION mode */
 +	ret = rtsn_change_mode(priv, OCR_OPC_OPERATION);
 +
 +	return ret;
 +}
 +
 +static int rtsn_mii_access(struct mii_bus *bus, bool read, int phyad,
 +			   int regad, u16 data)
 +{
 +	struct rtsn_private *priv = bus->priv;
 +	u32 val;
 +	int ret;
 +
 +	val = MPSM_PDA(phyad) | MPSM_PRA(regad) | MPSM_PSME;
 +
 +	if (!read)
 +		val |= MPSM_PSMAD | MPSM_PRD_SET(data);
 +
 +	rtsn_write(priv, MPSM, val);
 +
 +	ret = rtsn_reg_wait(priv, MPSM, MPSM_PSME, 0);
 +	if (ret)
 +		return ret;
 +
 +	if (read)
 +		ret = MPSM_PRD_GET(rtsn_read(priv, MPSM));
 +
 +	return ret;
 +}
 +
 +static int rtsn_mii_read(struct mii_bus *bus, int addr, int regnum)
 +{
 +	return rtsn_mii_access(bus, true, addr, regnum, 0);
 +}
 +
 +static int rtsn_mii_write(struct mii_bus *bus, int addr, int regnum, u16 val)
 +{
 +	return rtsn_mii_access(bus, false, addr, regnum, val);
 +}
 +
 +static int rtsn_mdio_alloc(struct rtsn_private *priv)
 +{
 +	struct platform_device *pdev = priv->pdev;
 +	struct device *dev = &pdev->dev;
 +	struct device_node *mdio_node;
 +	struct mii_bus *mii;
 +	int ret;
 +
 +	mii = mdiobus_alloc();
 +	if (!mii)
 +		return -ENOMEM;
 +
 +	mdio_node = of_get_child_by_name(dev->of_node, "mdio");
 +	if (!mdio_node) {
 +		ret = -ENODEV;
 +		goto out_free_bus;
 +	}
 +
 +	/* Enter config mode before registering the MDIO bus */
 +	ret = rtsn_reset(priv);
 +	if (ret)
 +		goto out_free_bus;
 +
 +	ret = rtsn_change_mode(priv, OCR_OPC_CONFIG);
 +	if (ret)
 +		goto out_free_bus;
 +
 +	rtsn_modify(priv, MPIC, MPIC_PSMCS_MASK | MPIC_PSMHT_MASK,
 +		    MPIC_PSMCS_DEFAULT | MPIC_PSMHT_DEFAULT);
 +
 +	/* Register the MDIO bus */
 +	mii->name = "rtsn_mii";
 +	snprintf(mii->id, MII_BUS_ID_SIZE, "%s-%x",
 +		 pdev->name, pdev->id);
 +	mii->priv = priv;
 +	mii->read = rtsn_mii_read;
 +	mii->write = rtsn_mii_write;
 +	mii->parent = dev;
 +
 +	ret = of_mdiobus_register(mii, mdio_node);
 +	of_node_put(mdio_node);
 +	if (ret)
 +		goto out_free_bus;
 +
 +	priv->mii = mii;
 +
 +	return 0;
 +
 +out_free_bus:
 +	mdiobus_free(mii);
 +	return ret;
 +}
 +
 +static void rtsn_mdio_free(struct rtsn_private *priv)
 +{
 +	mdiobus_unregister(priv->mii);
 +	mdiobus_free(priv->mii);
 +	priv->mii = NULL;
 +}
 +
 +static void rtsn_adjust_link(struct net_device *ndev)
 +{
 +	struct rtsn_private *priv = netdev_priv(ndev);
 +	struct phy_device *phydev = ndev->phydev;
 +	bool new_state = false;
 +	unsigned long flags;
 +
 +	spin_lock_irqsave(&priv->lock, flags);
 +
 +	if (phydev->link) {
 +		if (phydev->speed != priv->speed) {
 +			new_state = true;
 +			priv->speed = phydev->speed;
 +		}
 +
 +		if (!priv->link) {
 +			new_state = true;
 +			priv->link = phydev->link;
 +		}
 +	} else if (priv->link) {
 +		new_state = true;
 +		priv->link = 0;
 +		priv->speed = 0;
 +	}
 +
 +	if (new_state) {
 +		/* Need to transition to CONFIG mode before reconfiguring and
 +		 * then back to the original mode. Any state change to/from
 +		 * CONFIG or OPERATION must go over DISABLED to stop Rx/Tx.
 +		 */
 +		enum rtsn_mode orgmode = rtsn_read_mode(priv);
 +
 +		/* Transit to CONFIG */
 +		if (orgmode != OCR_OPC_CONFIG) {
 +			if (orgmode != OCR_OPC_DISABLE &&
 +			    rtsn_change_mode(priv, OCR_OPC_DISABLE))
 +				goto out;
 +			if (rtsn_change_mode(priv, OCR_OPC_CONFIG))
 +				goto out;
 +		}
 +
 +		rtsn_set_rate(priv);
 +
 +		/* Transition to original mode */
 +		if (orgmode != OCR_OPC_CONFIG) {
 +			if (rtsn_change_mode(priv, OCR_OPC_DISABLE))
 +				goto out;
 +			if (orgmode != OCR_OPC_DISABLE &&
 +			    rtsn_change_mode(priv, orgmode))
 +				goto out;
 +		}
 +	}
 +out:
 +	spin_unlock_irqrestore(&priv->lock, flags);
 +
 +	if (new_state)
 +		phy_print_status(phydev);
 +}
 +
 +static int rtsn_phy_init(struct rtsn_private *priv)
 +{
 +	struct device_node *np = priv->ndev->dev.parent->of_node;
 +	struct phy_device *phydev;
 +	struct device_node *phy;
 +
 +	priv->link = 0;
 +
 +	phy = of_parse_phandle(np, "phy-handle", 0);
 +	if (!phy)
 +		return -ENOENT;
 +
 +	phydev = of_phy_connect(priv->ndev, phy, rtsn_adjust_link, 0,
 +				priv->iface);
 +	of_node_put(phy);
 +	if (!phydev)
 +		return -ENOENT;
 +
 +	/* Only support full-duplex mode */
 +	phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_10baseT_Half_BIT);
 +	phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_100baseT_Half_BIT);
 +	phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Half_BIT);
 +
 +	phy_attached_info(phydev);
 +
 +	return 0;
 +}
 +
 +static void rtsn_phy_deinit(struct rtsn_private *priv)
 +{
 +	phy_disconnect(priv->ndev->phydev);
 +	priv->ndev->phydev = NULL;
 +}
 +
 +static int rtsn_init(struct rtsn_private *priv)
 +{
 +	int ret;
 +
 +	ret = rtsn_desc_alloc(priv);
 +	if (ret)
 +		return ret;
 +
 +	ret = rtsn_dmac_init(priv);
 +	if (ret)
 +		goto error_free_desc;
 +
 +	ret = rtsn_hw_init(priv);
 +	if (ret)
 +		goto error_free_chain;
 +
 +	ret = rtsn_phy_init(priv);
 +	if (ret)
 +		goto error_free_chain;
 +
 +	ret = rtsn_request_irqs(priv);
 +	if (ret)
 +		goto error_free_phy;
 +
 +	return 0;
 +error_free_phy:
 +	rtsn_phy_deinit(priv);
 +error_free_chain:
 +	rtsn_chain_free(priv);
 +error_free_desc:
 +	rtsn_desc_free(priv);
 +	return ret;
 +}
 +
 +static void rtsn_deinit(struct rtsn_private *priv)
 +{
 +	rtsn_free_irqs(priv);
 +	rtsn_phy_deinit(priv);
 +	rtsn_chain_free(priv);
 +	rtsn_desc_free(priv);
 +}
 +
 +static void rtsn_parse_mac_address(struct device_node *np,
 +				   struct net_device *ndev)
 +{
 +	struct rtsn_private *priv = netdev_priv(ndev);
 +	u8 addr[ETH_ALEN];
 +	u32 mrmac0;
 +	u32 mrmac1;
 +
 +	/* Try to read address from Device Tree. */
 +	if (!of_get_mac_address(np, addr)) {
 +		eth_hw_addr_set(ndev, addr);
 +		return;
 +	}
 +
 +	/* Try to read address from device. */
 +	mrmac0 = rtsn_read(priv, MRMAC0);
 +	mrmac1 = rtsn_read(priv, MRMAC1);
 +
 +	addr[0] = (mrmac0 >>  8) & 0xff;
 +	addr[1] = (mrmac0 >>  0) & 0xff;
 +	addr[2] = (mrmac1 >> 24) & 0xff;
 +	addr[3] = (mrmac1 >> 16) & 0xff;
 +	addr[4] = (mrmac1 >>  8) & 0xff;
 +	addr[5] = (mrmac1 >>  0) & 0xff;
 +
 +	if (is_valid_ether_addr(addr)) {
 +		eth_hw_addr_set(ndev, addr);
 +		return;
 +	}
 +
 +	/* Fallback to a random address */
 +	eth_hw_addr_random(ndev);
 +}
 +
 +static int rtsn_open(struct net_device *ndev)
 +{
 +	struct rtsn_private *priv = netdev_priv(ndev);
 +	int ret;
 +
 +	napi_enable(&priv->napi);
 +
 +	ret = rtsn_init(priv);
 +	if (ret) {
 +		napi_disable(&priv->napi);
 +		return ret;
 +	}
 +
 +	phy_start(ndev->phydev);
 +
 +	netif_start_queue(ndev);
 +
 +	return 0;
 +}
 +
 +static int rtsn_stop(struct net_device *ndev)
 +{
 +	struct rtsn_private *priv = netdev_priv(ndev);
 +
 +	phy_stop(priv->ndev->phydev);
 +	napi_disable(&priv->napi);
 +	rtsn_change_mode(priv, OCR_OPC_DISABLE);
 +	rtsn_deinit(priv);
 +
 +	return 0;
 +}
 +
 +static netdev_tx_t rtsn_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 +{
 +	struct rtsn_private *priv = netdev_priv(ndev);
 +	struct rtsn_ext_desc *desc;
 +	int ret = NETDEV_TX_OK;
 +	unsigned long flags;
 +	dma_addr_t dma_addr;
 +	int entry;
 +
 +	spin_lock_irqsave(&priv->lock, flags);
 +
 +	/* Drop packet if it won't fit in a single descriptor. */
 +	if (skb->len >= TX_DS) {
 +		priv->stats.tx_dropped++;
 +		priv->stats.tx_errors++;
 +		goto out;
 +	}
 +
 +	if (priv->cur_tx - priv->dirty_tx > priv->num_tx_ring) {
 +		netif_stop_subqueue(ndev, 0);
 +		ret = NETDEV_TX_BUSY;
 +		goto out;
 +	}
 +
 +	if (skb_put_padto(skb, ETH_ZLEN))
 +		goto out;
 +
 +	dma_addr = dma_map_single(ndev->dev.parent, skb->data, skb->len,
 +				  DMA_TO_DEVICE);
 +	if (dma_mapping_error(ndev->dev.parent, dma_addr)) {
 +		dev_kfree_skb_any(skb);
 +		goto out;
 +	}
 +
 +	entry = priv->cur_tx % priv->num_tx_ring;
 +	priv->tx_skb[entry] = skb;
 +	desc = &priv->tx_ring[entry];
 +	desc->dptr = cpu_to_le32(dma_addr);
 +	desc->info_ds = cpu_to_le16(skb->len);
 +	desc->info1 = cpu_to_le64(skb->len);
 +
 +	if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) {
 +		skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
 +		priv->ts_tag++;
 +		desc->info_ds |= cpu_to_le16(TXC);
 +		desc->info = priv->ts_tag;
 +	}
 +
 +	skb_tx_timestamp(skb);
 +	dma_wmb();
 +
 +	desc->die_dt = DT_FSINGLE | D_DIE;
 +	priv->cur_tx++;
 +
 +	/* Start xmit */
 +	rtsn_write(priv, TRCR0, BIT(TX_CHAIN_IDX));
 +out:
 +	spin_unlock_irqrestore(&priv->lock, flags);
 +	return ret;
 +}
 +
 +static void rtsn_get_stats64(struct net_device *ndev,
 +			     struct rtnl_link_stats64 *storage)
 +{
 +	struct rtsn_private *priv = netdev_priv(ndev);
 +	*storage = priv->stats;
 +}
 +
 +static int rtsn_do_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
 +{
 +	if (!netif_running(ndev))
 +		return -ENODEV;
 +
 +	return phy_do_ioctl_running(ndev, ifr, cmd);
 +}
 +
 +static int rtsn_hwtstamp_get(struct net_device *ndev,
 +			     struct kernel_hwtstamp_config *config)
 +{
 +	struct rcar_gen4_ptp_private *ptp_priv;
 +	struct rtsn_private *priv;
 +
 +	if (!netif_running(ndev))
 +		return -ENODEV;
 +
 +	priv = netdev_priv(ndev);
 +	ptp_priv = priv->ptp_priv;
 +
 +	config->flags = 0;
 +
 +	config->tx_type =
 +		ptp_priv->tstamp_tx_ctrl ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
 +
 +	switch (ptp_priv->tstamp_rx_ctrl & RCAR_GEN4_RXTSTAMP_TYPE) {
 +	case RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT:
 +		config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
 +		break;
 +	case RCAR_GEN4_RXTSTAMP_TYPE_ALL:
 +		config->rx_filter = HWTSTAMP_FILTER_ALL;
 +		break;
 +	default:
 +		config->rx_filter = HWTSTAMP_FILTER_NONE;
 +		break;
 +	}
 +
 +	return 0;
 +}
 +
 +static int rtsn_hwtstamp_set(struct net_device *ndev,
 +			     struct kernel_hwtstamp_config *config,
 +			     struct netlink_ext_ack *extack)
 +{
 +	struct rcar_gen4_ptp_private *ptp_priv;
 +	struct rtsn_private *priv;
 +	u32 tstamp_rx_ctrl;
 +	u32 tstamp_tx_ctrl;
 +
 +	if (!netif_running(ndev))
 +		return -ENODEV;
 +
 +	priv = netdev_priv(ndev);
 +	ptp_priv = priv->ptp_priv;
 +
 +	if (config->flags)
 +		return -EINVAL;
 +
 +	switch (config->tx_type) {
 +	case HWTSTAMP_TX_OFF:
 +		tstamp_tx_ctrl = 0;
 +		break;
 +	case HWTSTAMP_TX_ON:
 +		tstamp_tx_ctrl = RCAR_GEN4_TXTSTAMP_ENABLED;
 +		break;
 +	default:
 +		return -ERANGE;
 +	}
 +
 +	switch (config->rx_filter) {
 +	case HWTSTAMP_FILTER_NONE:
 +		tstamp_rx_ctrl = 0;
 +		break;
 +	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
 +		tstamp_rx_ctrl = RCAR_GEN4_RXTSTAMP_ENABLED |
 +			RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT;
 +		break;
 +	default:
 +		config->rx_filter = HWTSTAMP_FILTER_ALL;
 +		tstamp_rx_ctrl = RCAR_GEN4_RXTSTAMP_ENABLED |
 +			RCAR_GEN4_RXTSTAMP_TYPE_ALL;
 +		break;
 +	}
 +
 +	ptp_priv->tstamp_tx_ctrl = tstamp_tx_ctrl;
 +	ptp_priv->tstamp_rx_ctrl = tstamp_rx_ctrl;
 +
 +	return 0;
 +}
 +
 +static const struct net_device_ops rtsn_netdev_ops = {
 +	.ndo_open		= rtsn_open,
 +	.ndo_stop		= rtsn_stop,
 +	.ndo_start_xmit		= rtsn_start_xmit,
 +	.ndo_get_stats64	= rtsn_get_stats64,
 +	.ndo_eth_ioctl		= rtsn_do_ioctl,
 +	.ndo_validate_addr	= eth_validate_addr,
 +	.ndo_set_mac_address	= eth_mac_addr,
 +	.ndo_hwtstamp_set	= rtsn_hwtstamp_set,
 +	.ndo_hwtstamp_get	= rtsn_hwtstamp_get,
 +};
 +
 +static int rtsn_get_ts_info(struct net_device *ndev,
 +			    struct kernel_ethtool_ts_info *info)
 +{
 +	struct rtsn_private *priv = netdev_priv(ndev);
 +
 +	info->phc_index = ptp_clock_index(priv->ptp_priv->clock);
 +	info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
 +		SOF_TIMESTAMPING_RX_SOFTWARE |
 +		SOF_TIMESTAMPING_SOFTWARE |
 +		SOF_TIMESTAMPING_TX_HARDWARE |
 +		SOF_TIMESTAMPING_RX_HARDWARE |
 +		SOF_TIMESTAMPING_RAW_HARDWARE;
 +	info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON);
 +	info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL);
 +
 +	return 0;
 +}
 +
 +static const struct ethtool_ops rtsn_ethtool_ops = {
 +	.nway_reset		= phy_ethtool_nway_reset,
 +	.get_link		= ethtool_op_get_link,
 +	.get_ts_info		= rtsn_get_ts_info,
 +	.get_link_ksettings	= phy_ethtool_get_link_ksettings,
 +	.set_link_ksettings	= phy_ethtool_set_link_ksettings,
 +};
 +
 +static const struct of_device_id rtsn_match_table[] = {
 +	{ .compatible = "renesas,r8a779g0-ethertsn", },
 +	{ /* Sentinel */ }
 +};
 +
 +MODULE_DEVICE_TABLE(of, rtsn_match_table);
 +
 +static int rtsn_probe(struct platform_device *pdev)
 +{
 +	struct rtsn_private *priv;
 +	struct net_device *ndev;
 +	struct resource *res;
 +	int ret;
 +
 +	ndev = alloc_etherdev_mqs(sizeof(struct rtsn_private), TX_NUM_CHAINS,
 +				  RX_NUM_CHAINS);
 +	if (!ndev)
 +		return -ENOMEM;
 +
 +	priv = netdev_priv(ndev);
 +	priv->pdev = pdev;
 +	priv->ndev = ndev;
 +	priv->ptp_priv = rcar_gen4_ptp_alloc(pdev);
 +
 +	spin_lock_init(&priv->lock);
 +	platform_set_drvdata(pdev, priv);
 +
 +	priv->clk = devm_clk_get(&pdev->dev, NULL);
 +	if (IS_ERR(priv->clk)) {
 +		ret = PTR_ERR(priv->clk);
 +		goto error_free;
 +	}
 +
 +	priv->reset = devm_reset_control_get(&pdev->dev, NULL);
 +	if (IS_ERR(priv->reset)) {
 +		ret = PTR_ERR(priv->reset);
 +		goto error_free;
 +	}
 +
 +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tsnes");
 +	if (!res) {
 +		dev_err(&pdev->dev, "Can't find tsnes resource\n");
 +		ret = -EINVAL;
 +		goto error_free;
 +	}
 +
 +	priv->base = devm_ioremap_resource(&pdev->dev, res);
 +	if (IS_ERR(priv->base)) {
 +		ret = PTR_ERR(priv->base);
 +		goto error_free;
 +	}
 +
 +	SET_NETDEV_DEV(ndev, &pdev->dev);
 +
 +	ndev->features = NETIF_F_RXCSUM;
 +	ndev->hw_features = NETIF_F_RXCSUM;
 +	ndev->base_addr = res->start;
 +	ndev->netdev_ops = &rtsn_netdev_ops;
 +	ndev->ethtool_ops = &rtsn_ethtool_ops;
 +
 +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gptp");
 +	if (!res) {
 +		dev_err(&pdev->dev, "Can't find gptp resource\n");
 +		ret = -EINVAL;
 +		goto error_free;
 +	}
 +
 +	priv->ptp_priv->addr = devm_ioremap_resource(&pdev->dev, res);
 +	if (IS_ERR(priv->ptp_priv->addr)) {
 +		ret = PTR_ERR(priv->ptp_priv->addr);
 +		goto error_free;
 +	}
 +
 +	ret = rtsn_get_phy_params(priv);
 +	if (ret)
 +		goto error_free;
 +
 +	pm_runtime_enable(&pdev->dev);
 +	pm_runtime_get_sync(&pdev->dev);
 +
 +	netif_napi_add(ndev, &priv->napi, rtsn_poll);
 +
 +	rtsn_parse_mac_address(pdev->dev.of_node, ndev);
 +
 +	dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
 +
 +	device_set_wakeup_capable(&pdev->dev, 1);
 +
 +	ret = rcar_gen4_ptp_register(priv->ptp_priv, RCAR_GEN4_PTP_REG_LAYOUT,
 +				     clk_get_rate(priv->clk));
 +	if (ret)
 +		goto error_pm;
 +
 +	ret = rtsn_mdio_alloc(priv);
 +	if (ret)
 +		goto error_ptp;
 +
 +	ret = register_netdev(ndev);
 +	if (ret)
 +		goto error_mdio;
 +
 +	netdev_info(ndev, "MAC address %pM\n", ndev->dev_addr);
 +
 +	return 0;
 +
 +error_mdio:
 +	rtsn_mdio_free(priv);
 +error_ptp:
 +	rcar_gen4_ptp_unregister(priv->ptp_priv);
 +error_pm:
 +	netif_napi_del(&priv->napi);
 +	rtsn_change_mode(priv, OCR_OPC_DISABLE);
 +	pm_runtime_put_sync(&pdev->dev);
 +	pm_runtime_disable(&pdev->dev);
 +error_free:
 +	free_netdev(ndev);
 +
 +	return ret;
 +}
 +
- static int rtsn_remove(struct platform_device *pdev)
++static void rtsn_remove(struct platform_device *pdev)
 +{
 +	struct rtsn_private *priv = platform_get_drvdata(pdev);
 +
 +	unregister_netdev(priv->ndev);
 +	rtsn_mdio_free(priv);
 +	rcar_gen4_ptp_unregister(priv->ptp_priv);
 +	rtsn_change_mode(priv, OCR_OPC_DISABLE);
 +	netif_napi_del(&priv->napi);
 +
 +	pm_runtime_put_sync(&pdev->dev);
 +	pm_runtime_disable(&pdev->dev);
 +
 +	free_netdev(priv->ndev);
-
- 	return 0;
 +}
 +
 +static struct platform_driver rtsn_driver = {
 +	.probe		= rtsn_probe,
 +	.remove		= rtsn_remove,
 +	.driver	= {
 +		.name	= "rtsn",
 +		.of_match_table	= rtsn_match_table,
 +	}
 +};
 +module_platform_driver(rtsn_driver);
 +
 +MODULE_AUTHOR("Phong Hoang, Niklas Söderlund");
 +MODULE_DESCRIPTION("Renesas Ethernet-TSN device driver");
 +MODULE_LICENSE("GPL");
diff --cc drivers/reset/reset-meson-audio-arb.c
index 894ad9d37a66,8740f5f6abf8..421ccb40da8c
--- a/drivers/reset/reset-meson-audio-arb.c
+++ b/drivers/reset/reset-meson-audio-arb.c
@@@ -128,8 -128,8 +128,6 @@@ static void meson_audio_arb_remove(stru
  	spin_lock(&arb->lock);
  	writel(0, arb->regs);
  	spin_unlock(&arb->lock);
--
- 	return 0;
 -	clk_disable_unprepare(arb->clk);
  }

  static int meson_audio_arb_probe(struct platform_device *pdev)
diff --cc drivers/reset/reset-rzg2l-usbphy-ctrl.c
index 255c894a4782,8f6fbd978591..1cd157f4f03b
--- a/drivers/reset/reset-rzg2l-usbphy-ctrl.c
+++ b/drivers/reset/reset-rzg2l-usbphy-ctrl.c
@@@ -158,41 -153,10 +158,41 @@@ static int rzg2l_usbphy_ctrl_probe(stru
  	writel(val, priv->base + RESET);
  	spin_unlock_irqrestore(&priv->lock, flags);

 +	priv->rcdev.ops = &rzg2l_usbphy_ctrl_reset_ops;
 +	priv->rcdev.of_reset_n_cells = 1;
 +	priv->rcdev.nr_resets = NUM_PORTS;
 +	priv->rcdev.of_node = dev->of_node;
 +	priv->rcdev.dev = dev;
 +
 +	error = devm_reset_controller_register(dev, &priv->rcdev);
 +	if (error)
 +		goto err_pm_runtime_put;
 +
 +	vdev = platform_device_alloc("rzg2l-usb-vbus-regulator", pdev->id);
 +	if (!vdev) {
 +		error = -ENOMEM;
 +		goto err_pm_runtime_put;
 +	}
 +	vdev->dev.parent = dev;
 +	priv->vdev = vdev;
 +
 +	error = platform_device_add(vdev);
 +	if (error)
 +		goto err_device_put;
 +
  	return 0;
 +
 +err_device_put:
 +	platform_device_put(vdev);
 +err_pm_runtime_put:
 +	pm_runtime_put(&pdev->dev);
 +err_pm_disable_reset_deassert:
 +	pm_runtime_disable(&pdev->dev);
 +	reset_control_assert(priv->rstc);
 +	return error;
  }

- static int rzg2l_usbphy_ctrl_remove(struct platform_device *pdev)
+ static void rzg2l_usbphy_ctrl_remove(struct platform_device *pdev)
  {
  	struct rzg2l_usbphy_ctrl_priv *priv = dev_get_drvdata(&pdev->dev);

diff --cc rust/bindings/bindings_helper.h
index 6deee85a29c8,18a3f05115cb..53c996e4bedf
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@@ -7,11 -7,9 +7,12 @@@
   */

  #include <kunit/test.h>
 +#include <linux/blk_types.h>
 +#include <linux/blk-mq.h>
 +#include <linux/blkdev.h>
  #include <linux/errname.h>
  #include <linux/ethtool.h>
+ #include <linux/firmware.h>
  #include <linux/jiffies.h>
  #include <linux/mdio.h>
  #include <linux/phy.h>
diff --cc rust/kernel/lib.rs
index 2cf7c6b6f66b,7707cb013ce9..e6b7d3a80bbc
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@@ -27,10 -27,11 +27,13 @@@ compile_error!("Missing kernel configur
  extern crate self as kernel;

  pub mod alloc;
 +#[cfg(CONFIG_BLOCK)]
 +pub mod block;
  mod build_assert;
+ pub mod device;
  pub mod error;
+ #[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]
+ pub mod firmware;
  pub mod init;
  pub mod ioctl;
  #[cfg(CONFIG_KUNIT)]