From 76f1a34c4e44da78fc60ddf908a4a92e7c2e8037 Mon Sep 17 00:00:00 2001 From: Jianwei Fan Date: Thu, 5 Dec 2024 09:37:57 +0800 Subject: [PATCH 01/17] video: rockchip: vehicle: move xvclk ctrl to generic sensor Signed-off-by: Jianwei Fan Change-Id: If2b0e2e9ace5ef1835807471d8488e05cd2b8652 --- drivers/video/rockchip/vehicle/vehicle_ad.h | 3 +- drivers/video/rockchip/vehicle/vehicle_cif.c | 166 ++++++------------ drivers/video/rockchip/vehicle/vehicle_cif.h | 1 - .../rockchip/vehicle/vehicle_generic_sensor.c | 32 +++- drivers/video/rockchip/vehicle/vehicle_main.c | 7 +- 5 files changed, 86 insertions(+), 123 deletions(-) diff --git a/drivers/video/rockchip/vehicle/vehicle_ad.h b/drivers/video/rockchip/vehicle/vehicle_ad.h index a83233b791e0..dd189459145d 100644 --- a/drivers/video/rockchip/vehicle/vehicle_ad.h +++ b/drivers/video/rockchip/vehicle/vehicle_ad.h @@ -64,6 +64,7 @@ struct vehicle_ad_dev { u8 detect_status; u8 last_detect_status; int drop_frames; + struct clk *xvclk; }; int vehicle_generic_sensor_write(struct vehicle_ad_dev *ad, char reg, char *pval); @@ -74,7 +75,7 @@ int vehicle_parse_sensor(struct vehicle_ad_dev *ad); void vehicle_ad_channel_set(struct vehicle_ad_dev *ad, int channel); int vehicle_ad_init(struct vehicle_ad_dev *ad); -int vehicle_ad_deinit(void); +int vehicle_ad_deinit(struct vehicle_ad_dev *ad); int vehicle_ad_stream(struct vehicle_ad_dev *ad, int val); struct vehicle_cfg *vehicle_ad_get_vehicle_cfg(void); void vehicle_ad_check_cif_error(struct vehicle_ad_dev *ad, int last_line); diff --git a/drivers/video/rockchip/vehicle/vehicle_cif.c b/drivers/video/rockchip/vehicle/vehicle_cif.c index 082ed3b92881..85a441b1a6ba 100644 --- a/drivers/video/rockchip/vehicle/vehicle_cif.c +++ b/drivers/video/rockchip/vehicle/vehicle_cif.c @@ -3313,33 +3313,7 @@ err: return ret; } -/* sensor mclk set */ -static void rkcif_s_mclk(struct vehicle_cif *cif, int on, int clk_rate) -{ - int err = 0; - struct device *dev = cif->dev; - struct rk_cif_clk *clk = &cif->clk; - - //return ; - if (on && !clk->on) { - if (!IS_ERR(clk->xvclk)) { - err = clk_set_rate(clk->xvclk, clk_rate); - if (err < 0) - dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); - } - if (!IS_ERR(clk->xvclk)) { - err = clk_prepare_enable(clk->xvclk); - if (err < 0) - dev_err(dev, "Failed to enable xvclk\n"); - } - } else { - if (!IS_ERR(clk->xvclk)) - clk_disable_unprepare(clk->xvclk); - } - usleep_range(2000, 5000); -} - -static int rk_cif_mclk_ctrl(struct vehicle_cif *cif, int on, int clk_rate) +static int rk_cif_mclk_ctrl(struct vehicle_cif *cif, int on) { int err = 0; @@ -5130,36 +5104,14 @@ int vehicle_cif_reverse_close(void) return 0; } -static void vehicle_cif_dphy_get_node(struct vehicle_cif *cif) -{ - struct device_node *node = NULL; - struct device_node *cp = NULL; - struct device *dev = cif->dev; - const char *status = NULL; - - node = of_parse_phandle(dev->of_node, "rockchip,cif-phy", 0); - if (!node) { - VEHICLE_DGERR("get cif-phy dts failed\n"); - return; - } - - for_each_child_of_node(node, cp) { - of_property_read_string(cp, "status", &status); - if (status && !strcmp(status, "disabled")) - continue; - else - cif->phy_node = cp; - VEHICLE_INFO("status: %s %s\n", cp->name, status); - } -} - static int cif_parse_dt(struct vehicle_cif *cif) { struct device *dev = cif->dev; struct device_node *node; - struct device_node *phy_node = cif->phy_node; + struct device_node *phy_node; struct device_node *cif_node; struct device_node *cis2_node; + const char *status = NULL; if (of_property_read_u32(dev->of_node, "cif,drop-frames", &cif->drop_frames)) { @@ -5178,127 +5130,112 @@ static int cif_parse_dt(struct vehicle_cif *cif) node = of_parse_phandle(dev->of_node, "rockchip,cru", 0); cif->cru_base = of_iomap(node, 0); + of_node_put(node); node = of_parse_phandle(dev->of_node, "rockchip,grf", 0); cif->grf_base = of_iomap(node, 0); + of_node_put(node); cif->regmap_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); if (IS_ERR(cif->regmap_grf)) VEHICLE_DGERR("unable to get rockchip,grf\n"); cif->irq = irq_of_parse_and_map(cif_node, 0); + of_node_put(cif_node); if (cif->irq < 0) { VEHICLE_DGERR("%s: request cif irq failed\n", __func__); - iounmap(cif->base); - iounmap(cif->cru_base); - iounmap(cif->grf_base); - return -ENODEV; + goto unmap_cif; } - if (of_property_read_u32(phy_node, "csihost-idx", &cif->csi_host_idx)) { + node = of_parse_phandle(dev->of_node, "rockchip,cif-phy", 0); + if (!node) { + VEHICLE_DGERR("get cif-phy dts failed\n"); + goto unmap_cif; + } + + for_each_child_of_node(node, phy_node) { + of_property_read_string(phy_node, "status", &status); + if (status && !strcmp(status, "disabled")) + continue; + else + cif->phy_node = phy_node; + VEHICLE_INFO("status: %s %s\n", cif->phy_node->name, status); + } + of_node_put(node); + if (of_property_read_u32(cif->phy_node, "csihost-idx", &cif->csi_host_idx)) { VEHICLE_INFO("Get %s csihost-idx failed! sensor link to dvp!!\n", - phy_node->name); + cif->phy_node->name); cif->inf_id = RKCIF_DVP; } else { cif->inf_id = RKCIF_MIPI_LVDS; - VEHICLE_INFO("sensor link to %s!!\n", phy_node->name); + VEHICLE_INFO("sensor link to %s!!\n", cif->phy_node->name); } if (cif->inf_id == RKCIF_MIPI_LVDS) { if (cif->chip_id == CHIP_RK3588_VEHICLE_CIF && !(cif->csi_host_idx == RKCIF_MIPI0_CSI2 || cif->csi_host_idx == RKCIF_MIPI1_CSI2)) { - node = of_parse_phandle(phy_node, "rockchip,csi2-dphy", 0); + node = of_parse_phandle(cif->phy_node, "rockchip,csi2-dphy", 0); cif->csi2_dphy_base = of_iomap(node, 0); + of_node_put(node); cif->regmap_dphy_grf = - syscon_regmap_lookup_by_phandle(phy_node, "rockchip,dphy-grf"); + syscon_regmap_lookup_by_phandle(cif->phy_node, "rockchip,dphy-grf"); if (IS_ERR(cif->regmap_dphy_grf)) VEHICLE_INFO("unable to get rockchip,dphy-grf\n"); } else if (cif->chip_id == CHIP_RK3576_VEHICLE_CIF && cif->csi_host_idx != RKCIF_MIPI0_CSI2) { - node = of_parse_phandle(phy_node, "rockchip,csi2-dphy", 0); + node = of_parse_phandle(cif->phy_node, "rockchip,csi2-dphy", 0); cif->csi2_dphy_base = of_iomap(node, 0); + of_node_put(node); cif->regmap_dphy_grf = - syscon_regmap_lookup_by_phandle(phy_node, "rockchip,dphy-grf"); + syscon_regmap_lookup_by_phandle(cif->phy_node, "rockchip,dphy-grf"); if (IS_ERR(cif->regmap_dphy_grf)) VEHICLE_INFO("unable to get rockchip,dphy-grf\n"); cif->dphy_sys_grf = - syscon_regmap_lookup_by_phandle(phy_node, "rockchip,sys-grf"); + syscon_regmap_lookup_by_phandle(cif->phy_node, "rockchip,sys-grf"); if (IS_ERR(cif->dphy_sys_grf)) VEHICLE_INFO("unable to get rockchip,sys-grf\n"); } else if (cif->chip_id != CHIP_RK3588_VEHICLE_CIF && cif->chip_id != CHIP_RK3576_VEHICLE_CIF) { - node = of_parse_phandle(phy_node, "rockchip,csi2-dphy", 0); + node = of_parse_phandle(cif->phy_node, "rockchip,csi2-dphy", 0); cif->csi2_dphy_base = of_iomap(node, 0); + of_node_put(node); } - cis2_node = of_parse_phandle(phy_node, "rockchip,csi2", 0); + cis2_node = of_parse_phandle(cif->phy_node, "rockchip,csi2", 0); cif->csi2_base = of_iomap(cis2_node, 0); cif->csi2_irq1 = irq_of_parse_and_map(cis2_node, 0); if (cif->csi2_irq1 < 0) { VEHICLE_DGERR("%s: request csi-intr1 failed\n", __func__); - iounmap(cif->base); - iounmap(cif->cru_base); - iounmap(cif->grf_base); - iounmap(cif->csi2_dphy_base); - iounmap(cif->csi2_base); - return -ENODEV; + of_node_put(cis2_node); + goto unmap_csi; } cif->csi2_irq2 = irq_of_parse_and_map(cis2_node, 1); + of_node_put(cis2_node); if (cif->csi2_irq2 < 0) { VEHICLE_DGERR("%s: request csi-intr2 failed\n", __func__); - iounmap(cif->base); - iounmap(cif->cru_base); - iounmap(cif->grf_base); - iounmap(cif->csi2_dphy_base); - iounmap(cif->csi2_base); - return -ENODEV; + goto unmap_csi; } } VEHICLE_DG("%s, drop_frames = %d\n", __func__, cif->drop_frames); return 0; -} -int vehicle_cif_init_mclk(struct vehicle_cif *cif) -{ - struct device *dev = cif->dev; - struct rk_cif_clk *clk = &cif->clk; +unmap_csi: + iounmap(cif->csi2_dphy_base); + iounmap(cif->csi2_base); +unmap_cif: + iounmap(cif->base); + iounmap(cif->cru_base); + iounmap(cif->grf_base); - /* sensor MCLK: - * current use CLK_CIF_OUT - */ - vehicle_cif_dphy_get_node(cif); - clk->xvclk = of_clk_get_by_name(cif->phy_node, "xvclk"); - if (IS_ERR(clk->xvclk)) { - dev_err(dev, "Failed to get sensor xvclk\n"); - return -EINVAL; - } - - rkcif_s_mclk(cif, 1, 24000000); - VEHICLE_INFO("%s(%d): set sensor MCLK rate 24MHZ OK!\n", __func__, __LINE__); - - return 0; -} - -static int vehicle_cif_deinit_mclk(struct vehicle_cif *cif) -{ - struct rk_cif_clk *clk = &cif->clk; - - /* release sensor MCLK: - * current use CLK_CIF_OUT - */ - if (!IS_ERR(clk->xvclk)) - clk_disable_unprepare(clk->xvclk); - clk_put(clk->xvclk); - - return 0; + return -ENODEV; } int vehicle_cif_init(struct vehicle_cif *cif) @@ -5553,7 +5490,7 @@ int vehicle_cif_init(struct vehicle_cif *cif) } /* 2. set cif clk & sensor mclk */ - rk_cif_mclk_ctrl(cif, 1, 24000000); + rk_cif_mclk_ctrl(cif, 1); INIT_DELAYED_WORK(&cif->work, vehicle_cif_reset_work_func); if (inf_id == RKCIF_MIPI_LVDS) @@ -5665,15 +5602,12 @@ int vehicle_cif_deinit(struct vehicle_cif *cif) // vehicle_cif_s_stream(cif, 0); // vehicle_cif_do_stop_stream(cif); - /* set csi2-dphy csi cif clk & sensor mclk */ - rk_cif_mclk_ctrl(cif, 0, 0); + /* set csi2-dphy csi cif clk */ + rk_cif_mclk_ctrl(cif, 0); if (inf_id == RKCIF_MIPI_LVDS) if (cif->dphy_hw->on) vehicle_csi2_clk_ctrl(cif, 0); - /* release sensor MCLK */ - vehicle_cif_deinit_mclk(cif); - /* vicap rsts release */ for (i = 0; i < clk->rsts_num; i++) reset_control_put(clk->cif_rst[i]); diff --git a/drivers/video/rockchip/vehicle/vehicle_cif.h b/drivers/video/rockchip/vehicle/vehicle_cif.h index 48481034c92a..372a30da80a2 100644 --- a/drivers/video/rockchip/vehicle/vehicle_cif.h +++ b/drivers/video/rockchip/vehicle/vehicle_cif.h @@ -37,7 +37,6 @@ struct vehicle_rkcif_dummy_buffer { struct rk_cif_clk { /************clk************/ struct clk *clks[RKCIF_MAX_BUS_CLK]; - struct clk *xvclk; int clks_num; /************reset************/ struct reset_control *cif_rst[RKCIF_MAX_RESET]; diff --git a/drivers/video/rockchip/vehicle/vehicle_generic_sensor.c b/drivers/video/rockchip/vehicle/vehicle_generic_sensor.c index 32102d7cdcb0..e277e61ddc92 100644 --- a/drivers/video/rockchip/vehicle/vehicle_generic_sensor.c +++ b/drivers/video/rockchip/vehicle/vehicle_generic_sensor.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include "vehicle_ad.h" #include "vehicle_ad_7181.h" #include "vehicle_ad_tp2855.h" @@ -328,6 +330,12 @@ int vehicle_parse_sensor(struct vehicle_ad_dev *ad) ad->reset = of_get_named_gpio_flags(cp, "reset-gpios", 0, &flags); + ad->xvclk = of_clk_get_by_name(cp, "xvclk"); + if (IS_ERR(ad->xvclk)) { + ad->xvclk = NULL; + VEHICLE_DGERR("Failed to get sensor xvclk, maybe unuse\n"); + } + if (of_property_read_u32(cp, "i2c_add", &ad->i2c_add)) VEHICLE_DGERR("Get %s i2c_add failed!\n", cp->name); @@ -382,6 +390,24 @@ int vehicle_parse_sensor(struct vehicle_ad_dev *ad) return ret; } +static void vehicle_ad_mclk_set(struct vehicle_ad_dev *ad, int on) +{ + int err = 0; + int clk_rate = ad->mclk_rate * 1000000; + + if (on) { + err = clk_set_rate(ad->xvclk, clk_rate); + if (err < 0) + VEHICLE_DGERR("Failed to set xvclk rate (%dMHz)\n", ad->mclk_rate); + clk_prepare_enable(ad->xvclk); + if (err < 0) + VEHICLE_DGERR("Failed to enable xvclk\n"); + } else { + clk_disable_unprepare(ad->xvclk); + } + usleep_range(2000, 5000); +} + void vehicle_ad_channel_set(struct vehicle_ad_dev *ad, int channel) { if (sensor_cb && sensor_cb->sensor_set_channel) @@ -394,6 +420,7 @@ int vehicle_ad_init(struct vehicle_ad_dev *ad) //WARN_ON(1); VEHICLE_DGERR("%s(%d) ad_name:%s!", __func__, __LINE__, ad->ad_name); + vehicle_ad_mclk_set(ad, 1); if (sensor_cb && sensor_cb->sensor_init) { ret = sensor_cb->sensor_init(ad); if (ret < 0) { @@ -416,7 +443,7 @@ end: return ret; } -int vehicle_ad_deinit(void) +int vehicle_ad_deinit(struct vehicle_ad_dev *ad) { int ret = 0; @@ -425,6 +452,9 @@ int vehicle_ad_deinit(void) else ret = -EINVAL; + clk_disable_unprepare(ad->xvclk); + clk_put(ad->xvclk); + return ret; } diff --git a/drivers/video/rockchip/vehicle/vehicle_main.c b/drivers/video/rockchip/vehicle/vehicle_main.c index dc9320034200..e306c40ab16e 100644 --- a/drivers/video/rockchip/vehicle/vehicle_main.c +++ b/drivers/video/rockchip/vehicle/vehicle_main.c @@ -426,9 +426,8 @@ static int rk_vehicle_system_main(void *arg) /* 1.ad */ VEHICLE_DG("%s: vehicle_ad_init start\r\n", __func__); - /* config mclk first */ - ret = vehicle_cif_init_mclk(&v->cif); - ret |= vehicle_ad_init(&v->ad); + + ret = vehicle_ad_init(&v->ad); if (ret < 0) { VEHICLE_DGERR("%s: ad init failed\r\n", __func__); goto VEHICLE_AD_DEINIT; @@ -464,7 +463,7 @@ VEHICLE_CIF_DEINIT: vehicle_cif_deinit(&v->cif); VEHICLE_AD_DEINIT: - vehicle_ad_deinit(); + vehicle_ad_deinit(&v->ad); VEHICLE_GPIO_DEINIT: vehicle_gpio_deinit(&v->gpio_data); From 4a555cd7f1b3f070eaee649339b0ee17921281c4 Mon Sep 17 00:00:00 2001 From: Jianwei Fan Date: Thu, 5 Dec 2024 09:49:09 +0800 Subject: [PATCH 02/17] arm64: dts: rockchip: rk3576-evb1: image reverse move xvclk to sensor node Signed-off-by: Jianwei Fan Change-Id: Id6873da7d8da262ff138beb560eb608c2c6baf0a --- ...rk3576-evb1-v10-image-reverse-vehicle.dtsi | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3576-evb1-v10-image-reverse-vehicle.dtsi b/arch/arm64/boot/dts/rockchip/rk3576-evb1-v10-image-reverse-vehicle.dtsi index 06de135ed768..5c2caea977c9 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576-evb1-v10-image-reverse-vehicle.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3576-evb1-v10-image-reverse-vehicle.dtsi @@ -94,12 +94,10 @@ csi2_dcphy0 { status = "disabled"; - clocks = <&cru CLK_MIPI_CAMERAOUT_M0>, - <&cru PCLK_MIPI_DCPHY>, + clocks = <&cru PCLK_MIPI_DCPHY>, <&cru PCLK_CSI_HOST_0>, <&cru ICLK_CSIHOST0>; - clock-names = "xvclk", - "pclk", + clock-names = "pclk", "pclk_csi2host", "iclk_csi2host"; resets = <&cru SRST_P_CSI_HOST_0>; @@ -111,11 +109,9 @@ }; csi2_dphy0 { status = "disabled"; - clocks = <&cru CLK_MIPI_CAMERAOUT_M1>, - <&cru PCLK_CSIDPHY>, + clocks = <&cru PCLK_CSIDPHY>, <&cru PCLK_CSI_HOST_1>; - clock-names = "xvclk", - "pclk", + clock-names = "pclk", "pclk_csi2host"; resets = <&cru SRST_P_CSIPHY>, <&cru SRST_P_CSI_HOST_1>; @@ -129,14 +125,12 @@ }; csi2_dphy3 { status = "okay"; - clocks = <&cru CLK_MIPI_CAMERAOUT_M2>, - <&cru PCLK_CSIDPHY1>, - <&cru PCLK_CSI_HOST_3>; - clock-names = "xvclk", - "pclk", + clocks = <&cru PCLK_CSIDPHY1>, + <&cru PCLK_CSI_HOST_3>; + clock-names = "pclk", "pclk_csi2host"; resets = <&cru SRST_P_CSIDPHY1>, - <&cru SRST_P_CSI_HOST_3>; + <&cru SRST_P_CSI_HOST_3>; reset-names = "srst_p_csiphy", "srst_csihost_p"; csihost-idx = <3>; @@ -153,6 +147,8 @@ nvp6324 { status = "okay"; + clocks = <&cru CLK_MIPI_CAMERAOUT_M1>; + clock-names = "xvclk"; //dphy3 powerdown-gpios = <&gpio3 RK_PD5 GPIO_ACTIVE_HIGH>; pwdn_active = <1>; From 8da0472f8c6214cae516c5d215184e98c4d25f96 Mon Sep 17 00:00:00 2001 From: Jianwei Fan Date: Thu, 5 Dec 2024 09:53:03 +0800 Subject: [PATCH 03/17] arm64: dts: rockchip: rk3588-vehicle-evb: image reverse move xvclk to sensor Signed-off-by: Jianwei Fan Change-Id: I54a50c63fa1c3ef062eaba6098d7fe2e4a310698 --- .../rk3588-vehicle-evb-image-reverse.dtsi | 29 +++++++------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-image-reverse.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-image-reverse.dtsi index fa4255a273c8..eb226e926b30 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-image-reverse.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-image-reverse.dtsi @@ -77,12 +77,10 @@ csi2_dcphy0 { status = "disabled"; - clocks = <&cru CLK_MIPI_CAMARAOUT_M1>, - <&cru PCLK_MIPI_DCPHY0>, + clocks = <&cru PCLK_MIPI_DCPHY0>, <&cru PCLK_CSI_HOST_0>, <&cru ICLK_CSIHOST0>; - clock-names = "xvclk", - "pclk", + clock-names = "pclk", "pclk_csi2host", "iclk_csi2host"; resets = <&cru SRST_P_CSI_HOST_0>, @@ -96,12 +94,10 @@ }; csi2_dcphy1 { status = "disabled"; - clocks = <&cru CLK_MIPI_CAMARAOUT_M2>, - <&cru PCLK_MIPI_DCPHY1>, + clocks = <&cru PCLK_MIPI_DCPHY1>, <&cru PCLK_CSI_HOST_1>, <&cru ICLK_CSIHOST1>; - clock-names = "xvclk", - "pclk", + clock-names = "pclk", "pclk_csi2host", "iclk_csi2host"; resets = <&cru SRST_P_CSI_HOST_1>, @@ -115,11 +111,9 @@ }; csi2_dphy0 { status = "okay"; - clocks = <&cru CLK_MIPI_CAMARAOUT_M2>, - <&cru PCLK_CSIPHY0>, + clocks = <&cru PCLK_CSIPHY0>, <&cru PCLK_CSI_HOST_2>; - clock-names = "xvclk", - "pclk", + clock-names = "pclk", "pclk_csi2host"; resets = <&cru SRST_CSIPHY0>, <&cru SRST_P_CSIPHY0>, @@ -137,11 +131,9 @@ /* only rk3588 */ csi2_dphy3 { status = "disabled"; - clocks = <&cru CLK_MIPI_CAMARAOUT_M4>, - <&cru PCLK_CSIPHY1>, + clocks = <&cru PCLK_CSIPHY1>, <&cru PCLK_CSI_HOST_4>; - clock-names = "xvclk", - "pclk", + clock-names = "pclk", "pclk_csi2host"; resets = <&cru SRST_CSIPHY1>, <&cru SRST_P_CSIPHY1>, @@ -158,8 +150,6 @@ }; rkcif_dvp { status = "disabled"; - clocks = <&cru CLK_CIFOUT_OUT>; - clock-names = "xvclk"; }; }; @@ -170,7 +160,8 @@ nvp6188 { is_front = <0>; status = "okay"; - + clocks = <&cru CLK_MIPI_CAMARAOUT_M2>; + clock-names = "xvclk"; /*dphy0*/ powerdown-gpios = <&gpio3 RK_PB3 GPIO_ACTIVE_HIGH>; pwdn_active = <1>; From 9f8868d7b36502278ee4adaf90beb27b51af77d9 Mon Sep 17 00:00:00 2001 From: Elaine Zhang Date: Fri, 17 Jan 2025 11:26:25 +0800 Subject: [PATCH 04/17] arm64: dts: rockchip: rk3576: remove dma for can Signed-off-by: Elaine Zhang Change-Id: I4bb0152a2742fa9ca7cc2d7330f4f4200d4672e6 --- arch/arm64/boot/dts/rockchip/rk3576.dtsi | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3576.dtsi b/arch/arm64/boot/dts/rockchip/rk3576.dtsi index e25899a39eff..172122edae4f 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi @@ -5447,8 +5447,6 @@ clock-names = "baudclk", "apb_pclk"; resets = <&cru SRST_CAN0>, <&cru SRST_H_CAN0>; reset-names = "can", "can-apb"; - dmas = <&dmac0 20>; - dma-names = "rx"; status = "disabled"; }; @@ -5460,8 +5458,6 @@ clock-names = "baudclk", "apb_pclk"; resets = <&cru SRST_CAN1>, <&cru SRST_H_CAN1>; reset-names = "can", "can-apb"; - dmas = <&dmac1 21>; - dma-names = "rx"; status = "disabled"; }; From c790e2f91a31487159367fa45e1918f5fce64d15 Mon Sep 17 00:00:00 2001 From: Joseph Chen Date: Wed, 15 Jan 2025 15:53:26 +0800 Subject: [PATCH 05/17] regulator: rk801: Fix ramp_delay value error Signed-off-by: Joseph Chen Change-Id: Id35502968d0c3bf2b9a15b1008c2bc47e3ba8bcb --- drivers/regulator/rk801-regulator.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/rk801-regulator.c b/drivers/regulator/rk801-regulator.c index 349b2636ea3b..703db41c2bf3 100644 --- a/drivers/regulator/rk801-regulator.c +++ b/drivers/regulator/rk801-regulator.c @@ -341,7 +341,7 @@ static const struct regulator_desc rk801_reg[] = { .enable_mask = ENABLE_MASK(RK801_ID_DCDC1), .enable_val = ENABLE_VAL(RK801_ID_DCDC1), .disable_val = DISABLE_VAL(RK801_ID_DCDC1), - .ramp_delay = 1, + .ramp_delay = 1000, .of_map_mode = rk801_regulator_of_map_mode, .enable_time = 400, .owner = THIS_MODULE, @@ -362,7 +362,7 @@ static const struct regulator_desc rk801_reg[] = { .enable_mask = ENABLE_MASK(RK801_ID_DCDC2), .enable_val = ENABLE_VAL(RK801_ID_DCDC2), .disable_val = DISABLE_VAL(RK801_ID_DCDC2), - .ramp_delay = 1, + .ramp_delay = 1000, .of_map_mode = rk801_regulator_of_map_mode, .enable_time = 400, .owner = THIS_MODULE, @@ -399,7 +399,7 @@ static const struct regulator_desc rk801_reg[] = { .enable_mask = ENABLE_MASK(RK801_ID_DCDC4), .enable_val = ENABLE_VAL(RK801_ID_DCDC4), .disable_val = DISABLE_VAL(RK801_ID_DCDC4), - .ramp_delay = 1, + .ramp_delay = 1000, .of_map_mode = rk801_regulator_of_map_mode, .enable_time = 400, .owner = THIS_MODULE, From a3b5a023917f2dc82beb29d9562b817e0c5da240 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Thu, 9 Jan 2025 19:12:59 +0800 Subject: [PATCH 06/17] pwm: rockchip: add support to select clk_src and mem_clk_src for wave generator mode In wave generator mode, the clk_src is used as dclk to generate the wave, and the mem_clk_src is used to write/read the wave table. Change-Id: I7d1948a5bb11715ac632c424ef01d6022d15899b Signed-off-by: Damon Ding --- drivers/pwm/pwm-rockchip.c | 5 +++-- include/linux/pwm-rockchip.h | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index 820d26e42cb1..26a7986b6157 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -1599,7 +1599,8 @@ static int rockchip_pwm_set_wave_v4(struct pwm_chip *chip, struct pwm_device *pw ctrl = WAVE_DUTY_EN(config->duty_en) | WAVE_PERIOD_EN(config->period_en) | WAVE_WIDTH_MODE(config->width_mode) | - WAVE_UPDATE_MODE(config->update_mode); + WAVE_UPDATE_MODE(config->update_mode) | + WAVE_MEM_CLK_SEL(config->mem_clk_src); max_val = config->duty_max * factor << WAVE_DUTY_MAX_SHIFT | config->period_max * factor << WAVE_PERIOD_MAX_SHIFT; min_val = config->duty_min * factor << WAVE_DUTY_MIN_SHIFT | @@ -1613,7 +1614,7 @@ static int rockchip_pwm_set_wave_v4(struct pwm_chip *chip, struct pwm_device *pw ctrl = WAVE_DUTY_EN(false) | WAVE_PERIOD_EN(false); } - writel_relaxed(CLK_SCALE(pc->scaler), pc->base + CLK_CTRL); + writel_relaxed(CLK_SCALE(pc->scaler) | CLK_SRC_SEL(config->clk_src), pc->base + CLK_CTRL); writel_relaxed(ctrl, pc->base + WAVE_CTRL); writel_relaxed(max_val, pc->base + WAVE_MAX); writel_relaxed(min_val, pc->base + WAVE_MIN); diff --git a/include/linux/pwm-rockchip.h b/include/linux/pwm-rockchip.h index f3c550dcb2b0..9fe131df6ebc 100644 --- a/include/linux/pwm-rockchip.h +++ b/include/linux/pwm-rockchip.h @@ -8,6 +8,18 @@ #include +/** + * enum rockchip_pwm_clk_src_sel - select the clk src of pwm + * @PWM_SELECT_CLK_PWM: select clk_pwm as clk src + * @PWM_SELECT_CLK_PWM_OSC: select clk_pwm_osc as clk src + * @PWM_SELECT_CLK_PWM_RC: select clk_pwm_rc as clk src + */ +enum rockchip_pwm_clk_src_sel { + PWM_SELECT_CLK_PWM, + PWM_SELECT_CLK_PWM_OSC, + PWM_SELECT_CLK_PWM_RC, +}; + /** * enum rockchip_pwm_global_ctrl_cmd - commands for pwm global ctrl * @PWM_GLOBAL_CTRL_JOIN: join the global control group @@ -89,6 +101,8 @@ enum rockchip_pwm_wave_update_mode { * struct rockchip_pwm_wave_config - wave generator config object * @duty_table: the wave table config of duty * @period_table: the wave table config of period + * @clk_src: the clk src selection in wave generator mode + * @mem_clk_src: the memory clk src selection in wave generator mode * @enable: enable or disable wave generator * @duty_en: to update duty by duty table or not * @period_en: to update period by period table or not @@ -109,6 +123,8 @@ enum rockchip_pwm_wave_update_mode { struct rockchip_pwm_wave_config { struct rockchip_pwm_wave_table *duty_table; struct rockchip_pwm_wave_table *period_table; + enum rockchip_pwm_clk_src_sel clk_src; + enum rockchip_pwm_clk_src_sel mem_clk_src; bool enable; bool duty_en; bool period_en; From a16c42163b5f98365f0cb6b18486bfe231044c8d Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Mon, 20 Jan 2025 09:18:21 +0800 Subject: [PATCH 07/17] pwm: rockchip: use existing enum definitions for width_mode and updata_mode settings Change-Id: I3a1957e96b3f695849e6afffd3348a4a80635ad9 Signed-off-by: Damon Ding --- include/linux/pwm-rockchip.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/linux/pwm-rockchip.h b/include/linux/pwm-rockchip.h index 9fe131df6ebc..75c4f43abde7 100644 --- a/include/linux/pwm-rockchip.h +++ b/include/linux/pwm-rockchip.h @@ -103,13 +103,13 @@ enum rockchip_pwm_wave_update_mode { * @period_table: the wave table config of period * @clk_src: the clk src selection in wave generator mode * @mem_clk_src: the memory clk src selection in wave generator mode + * @width_mode: the width mode of wave table + * @update_mode: the update mode of wave generator * @enable: enable or disable wave generator * @duty_en: to update duty by duty table or not * @period_en: to update period by period table or not * @clk_rate: the dclk rate in wave generator mode * @rpt: the number of repeated effective periods - * @width_mode: the width mode of wave table - * @update_mode: the update mode of wave generator * @duty_max: the maximum address of duty table * @duty_min: the minimum address of duty table * @period_max: the maximum address of period table @@ -125,13 +125,13 @@ struct rockchip_pwm_wave_config { struct rockchip_pwm_wave_table *period_table; enum rockchip_pwm_clk_src_sel clk_src; enum rockchip_pwm_clk_src_sel mem_clk_src; + enum rockchip_pwm_wave_table_width_mode width_mode; + enum rockchip_pwm_wave_update_mode update_mode; bool enable; bool duty_en; bool period_en; unsigned long clk_rate; u16 rpt; - u32 width_mode; - u32 update_mode; u32 duty_max; u32 duty_min; u32 period_max; From ef369922f4a1be1daa018d52272a798de6b1b7db Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Sat, 4 Jan 2025 12:55:03 +0800 Subject: [PATCH 08/17] spi: rockchip_slave: Remove redundant dma_map behavior The driver itself has implemented DMA buffer, and does not require the map/unmap behavior of the spi framework. Change-Id: I00abd65233bf5b3112337c8da623bc8afe586bb6 Signed-off-by: Jon Lin --- drivers/spi/spi-rockchip-slave.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/spi/spi-rockchip-slave.c b/drivers/spi/spi-rockchip-slave.c index ed9e53bf13e6..58377839db0f 100644 --- a/drivers/spi/spi-rockchip-slave.c +++ b/drivers/spi/spi-rockchip-slave.c @@ -471,6 +471,20 @@ static int rockchip_spi_slave_prepare_dma(struct rockchip_spi *rs, return 1; } +static bool rockchip_spi_slave_can_dma(struct spi_controller *ctlr, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); + unsigned int bytes_per_word = xfer->bits_per_word <= 8 ? 1 : 2; + + /* if the numbor of spi words to transfer is less than the fifo + * length we can just fill the fifo and wait for a single irq, + * so don't bother setting up dma + */ + return xfer->len / bytes_per_word >= rs->fifo_len; +} + static int rockchip_spi_slave_config(struct rockchip_spi *rs, struct spi_device *spi, struct spi_transfer *xfer) { @@ -673,7 +687,7 @@ static int rockchip_spi_slave_do_one_msg(struct spi_controller *ctlr, struct spi rs->n_bytes = xfer->bits_per_word <= 8 ? 1 : 2; rs->xfer = xfer; - use_dma = ctlr->can_dma(ctlr, spi, xfer); + use_dma = rockchip_spi_slave_can_dma(ctlr, spi, xfer); if (use_dma) rs->xfer_mode = ROCKCHIP_SPI_DMA; else @@ -724,20 +738,6 @@ out: return 0; } -static bool rockchip_spi_slave_can_dma(struct spi_controller *ctlr, - struct spi_device *spi, - struct spi_transfer *xfer) -{ - struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); - unsigned int bytes_per_word = xfer->bits_per_word <= 8 ? 1 : 2; - - /* if the numbor of spi words to transfer is less than the fifo - * length we can just fill the fifo and wait for a single irq, - * so don't bother setting up dma - */ - return xfer->len / bytes_per_word >= rs->fifo_len; -} - static int rockchip_spi_slave_setup(struct spi_device *spi) { struct rockchip_spi *rs = spi_controller_get_devdata(spi->controller); @@ -885,7 +885,6 @@ static int rockchip_spi_slave_probe(struct platform_device *pdev) } rs->dma_addr_tx = mem->start + ROCKCHIP_SPI_TXDR; rs->dma_addr_rx = mem->start + ROCKCHIP_SPI_RXDR; - ctlr->can_dma = rockchip_spi_slave_can_dma; init_completion(&rs->xfer_done); switch (rs->version) { From 7fe6e5c9022857cfe0414f94f28f2291c7042751 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Thu, 9 Jan 2025 19:26:58 +0800 Subject: [PATCH 09/17] pwm: rockchip-test: set the default configurations for clk_src and mem_clk_src In addition, adjust the initialization order of struct rockchip_pwm_wave_config members to correspond with their declaration order within the struct. Change-Id: I869f1a670f23f726b2fd983af364d3bdd6a9f321 Signed-off-by: Damon Ding --- drivers/pwm/pwm-rockchip-test.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/pwm/pwm-rockchip-test.c b/drivers/pwm/pwm-rockchip-test.c index 59294260298d..757013141fe5 100644 --- a/drivers/pwm/pwm-rockchip-test.c +++ b/drivers/pwm/pwm-rockchip-test.c @@ -633,17 +633,17 @@ static ssize_t pwm_rockchip_test_write(struct file *file, const char __user *buf duty_table.offset = (channel_id % 3) * PWM_TABLE_MAX; duty_table.len = PWM_TABLE_MAX; - wave_config.rpt = PWM_WAVE_RPT; - - /* select the 400k clk src */ - wave_config.clk_rate = 400000; wave_config.duty_table = &duty_table; wave_config.period_table = NULL; + wave_config.clk_src = PWM_SELECT_CLK_PWM; + wave_config.mem_clk_src = PWM_SELECT_CLK_PWM_OSC; + wave_config.width_mode = PWM_WIDTH_MODE; + wave_config.update_mode = PWM_WAVE_INCREASING_THEN_DECREASING; wave_config.enable = enable; wave_config.duty_en = true; wave_config.period_en = false; - wave_config.width_mode = PWM_WIDTH_MODE; - wave_config.update_mode = PWM_WAVE_INCREASING_THEN_DECREASING; + wave_config.clk_rate = 400000; + wave_config.rpt = PWM_WAVE_RPT; wave_config.duty_max = (channel_id % 3 + 1) * PWM_TABLE_MAX - 1; wave_config.duty_min = (channel_id % 3) * PWM_TABLE_MAX; wave_config.period_max = 0; From 61fc6ee91fe5de5fc000c6bcaba51212c1a71bbc Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Thu, 9 Jan 2025 19:37:25 +0800 Subject: [PATCH 10/17] ARM: dts: rockchip: rk3506g-iotest: add wave clk_pwm_rc mode for pwm test Change-Id: I20a2719e79fc978e42d42d119de0429f5bcc6458 Signed-off-by: Damon Ding --- .../arm/boot/dts/rk3506g-iotest-pwm-test.dtsi | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm/boot/dts/rk3506g-iotest-pwm-test.dtsi b/arch/arm/boot/dts/rk3506g-iotest-pwm-test.dtsi index 0434275de148..18ec9d236b36 100644 --- a/arch/arm/boot/dts/rk3506g-iotest-pwm-test.dtsi +++ b/arch/arm/boot/dts/rk3506g-iotest-pwm-test.dtsi @@ -40,32 +40,52 @@ status = "okay"; pinctrl-names = "active"; pinctrl-0 = <&rm_io8_pwm0_ch0>; +#ifdef WAVE_CLK_PWM_RC_TEST + clocks = <&cru CLK_RC_PWM0>, <&cru PCLK_PWM0>, <&cru CLK_OSC_PWM0>; + clock-names = "pwm", "pclk", "osc"; +#else assigned-clocks = <&cru CLK_PWM0>; assigned-clock-rates = <100000000>; +#endif }; &pwm0_4ch_1 { status = "okay"; pinctrl-names = "active"; pinctrl-0 = <&rm_io9_pwm0_ch1>; +#ifdef WAVE_CLK_PWM_RC_TEST + clocks = <&cru CLK_RC_PWM0>, <&cru PCLK_PWM0>, <&cru CLK_OSC_PWM0>; + clock-names = "pwm", "pclk", "osc"; +#else assigned-clocks = <&cru CLK_PWM0>; assigned-clock-rates = <100000000>; +#endif }; &pwm0_4ch_2 { status = "okay"; pinctrl-names = "active"; pinctrl-0 = <&rm_io10_pwm0_ch2>; +#ifdef WAVE_CLK_PWM_RC_TEST + clocks = <&cru CLK_RC_PWM0>, <&cru PCLK_PWM0>, <&cru CLK_OSC_PWM0>; + clock-names = "pwm", "pclk", "osc"; +#else assigned-clocks = <&cru CLK_PWM0>; assigned-clock-rates = <100000000>; +#endif }; &pwm0_4ch_3 { status = "okay"; pinctrl-names = "active"; pinctrl-0 = <&rm_io11_pwm0_ch3>; +#ifdef WAVE_CLK_PWM_RC_TEST + clocks = <&cru CLK_RC_PWM0>, <&cru PCLK_PWM0>, <&cru CLK_OSC_PWM0>; + clock-names = "pwm", "pclk", "osc"; +#else assigned-clocks = <&cru CLK_PWM0>; assigned-clock-rates = <100000000>; +#endif }; &pwm1_8ch_0 { From 0e51e09b71c941ea2e79365bddc4dd82b0119661 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Fri, 3 Jan 2025 09:58:10 +0800 Subject: [PATCH 11/17] drm/bridge: analogix_dp: modify the log related to the check of hpd status The disconnected status is not actually a failure, so it is more reasonable to remove 'failed' and use dev_dbg() instead of dev_err(). Change-Id: I6c2fea479b45e6c61ce460d364efe8f3053465e2 Signed-off-by: Damon Ding --- drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index ede08c45b199..6ac47ad2cc36 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -178,7 +178,7 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp) analogix_dp_force_hpd(dp); if (analogix_dp_get_plug_in_status(dp) != 0) { - dev_err(dp->dev, "failed to get hpd plug in status\n"); + dev_dbg(dp->dev, "hpd status is detected as disconnected\n"); return -EINVAL; } From e18437f0e8d7218d02a21ec8ee89be745df10dd5 Mon Sep 17 00:00:00 2001 From: Zhibin Huang Date: Thu, 12 Dec 2024 10:45:16 +0800 Subject: [PATCH 12/17] misc: rk628: bt1120: modify bt1120 decoder dclk delay Modify bt1120 decoder dclk delay to improve compatibility with SI tested configurations, refer to redmine #525199 for specific details. Type: Fix Redmine ID: #525199 Associated modifications: N/A Test: N/A Signed-off-by: Zhibin Huang Change-Id: I1a288f9d56667899537b650d2a36dcc298095a6a --- drivers/misc/rk628/rk628_rgb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/rk628/rk628_rgb.c b/drivers/misc/rk628/rk628_rgb.c index a8aed6f90e87..ee0de851307e 100644 --- a/drivers/misc/rk628/rk628_rgb.c +++ b/drivers/misc/rk628/rk628_rgb.c @@ -294,7 +294,7 @@ static void rk628_bt1120_decoder_enable(struct rk628 *rk628) } else { if (rk628->version == RK628F_VERSION) { rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON0, - 0x08000000); + 0x1); rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON1, 0); } } From b088c5c203ae02f46c627d82594304cd56f7b4ae Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 20 Jun 2023 16:19:04 +0300 Subject: [PATCH 13/17] UPSTREAM: mtd: use refcount to prevent corruption When underlying device is removed mtd core will crash in case user space is holding open handle. Need to use proper refcounting so device is release only when has no users. Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230620131905.648089-2-alexander.usyskin@intel.com Signed-off-by: Jon Lin (cherry picked from commit 19bfa9ebebb5ec0695def57eb1d80de7e9cab369) Change-Id: I95b670fa24e6411867cfa517560802e58a183ef8 --- drivers/mtd/mtdcore.c | 72 ++++++++++++++++++++++------------------- drivers/mtd/mtdcore.h | 1 + drivers/mtd/mtdpart.c | 14 ++++---- include/linux/mtd/mtd.h | 2 +- 4 files changed, 49 insertions(+), 40 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index ad527bdbd463..565b948c99dd 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -92,10 +92,33 @@ static void mtd_release(struct device *dev) struct mtd_info *mtd = dev_get_drvdata(dev); dev_t index = MTD_DEVT(mtd->index); + if (mtd_is_partition(mtd)) + release_mtd_partition(mtd); + /* remove /dev/mtdXro node */ device_destroy(&mtd_class, index + 1); } +static void mtd_device_release(struct kref *kref) +{ + struct mtd_info *mtd = container_of(kref, struct mtd_info, refcnt); + + debugfs_remove_recursive(mtd->dbg.dfs_dir); + + /* Try to remove the NVMEM provider */ + nvmem_unregister(mtd->nvmem); + + device_unregister(&mtd->dev); + + /* Clear dev so mtd can be safely re-registered later if desired */ + memset(&mtd->dev, 0, sizeof(mtd->dev)); + + idr_remove(&mtd_idr, mtd->index); + of_node_put(mtd_get_of_node(mtd)); + + module_put(THIS_MODULE); +} + #define MTD_DEVICE_ATTR_RO(name) \ static DEVICE_ATTR(name, 0444, mtd_##name##_show, NULL) @@ -674,7 +697,7 @@ int add_mtd_device(struct mtd_info *mtd) } mtd->index = i; - mtd->usecount = 0; + kref_init(&mtd->refcnt); /* default value if not set by driver */ if (mtd->bitflip_threshold == 0) @@ -776,7 +799,6 @@ int del_mtd_device(struct mtd_info *mtd) { int ret; struct mtd_notifier *not; - struct device_node *mtd_of_node; mutex_lock(&mtd_table_mutex); @@ -790,28 +812,8 @@ int del_mtd_device(struct mtd_info *mtd) list_for_each_entry(not, &mtd_notifiers, list) not->remove(mtd); - if (mtd->usecount) { - printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n", - mtd->index, mtd->name, mtd->usecount); - ret = -EBUSY; - } else { - mtd_of_node = mtd_get_of_node(mtd); - debugfs_remove_recursive(mtd->dbg.dfs_dir); - - /* Try to remove the NVMEM provider */ - nvmem_unregister(mtd->nvmem); - - device_unregister(&mtd->dev); - - /* Clear dev so mtd can be safely re-registered later if desired */ - memset(&mtd->dev, 0, sizeof(mtd->dev)); - - idr_remove(&mtd_idr, mtd->index); - of_node_put(mtd_of_node); - - module_put(THIS_MODULE); - ret = 0; - } + kref_put(&mtd->refcnt, mtd_device_release); + ret = 0; out_error: mutex_unlock(&mtd_table_mutex); @@ -1210,19 +1212,21 @@ int __get_mtd_device(struct mtd_info *mtd) if (!try_module_get(master->owner)) return -ENODEV; + kref_get(&mtd->refcnt); + if (master->_get_device) { err = master->_get_device(mtd); if (err) { + kref_put(&mtd->refcnt, mtd_device_release); module_put(master->owner); return err; } } - master->usecount++; - while (mtd->parent) { - mtd->usecount++; + if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) || mtd->parent != master) + kref_get(&mtd->parent->refcnt); mtd = mtd->parent; } @@ -1309,18 +1313,20 @@ void __put_mtd_device(struct mtd_info *mtd) { struct mtd_info *master = mtd_get_master(mtd); - while (mtd->parent) { - --mtd->usecount; - BUG_ON(mtd->usecount < 0); - mtd = mtd->parent; - } + while (mtd != master) { + struct mtd_info *parent = mtd->parent; - master->usecount--; + kref_put(&mtd->refcnt, mtd_device_release); + mtd = parent; + } if (master->_put_device) master->_put_device(master); module_put(master->owner); + + if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) + kref_put(&master->refcnt, mtd_device_release); } EXPORT_SYMBOL_GPL(__put_mtd_device); diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h index b5eefeabf310..b014861a06a6 100644 --- a/drivers/mtd/mtdcore.h +++ b/drivers/mtd/mtdcore.h @@ -12,6 +12,7 @@ int __must_check add_mtd_device(struct mtd_info *mtd); int del_mtd_device(struct mtd_info *mtd); int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int); int del_mtd_partitions(struct mtd_info *); +void release_mtd_partition(struct mtd_info *mtd); struct mtd_partitions; diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 85f5ee6f06fc..73f9e371a04d 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -32,6 +32,12 @@ static inline void free_partition(struct mtd_info *mtd) kfree(mtd); } +void release_mtd_partition(struct mtd_info *mtd) +{ + WARN_ON(!list_empty(&mtd->part.node)); + free_partition(mtd); +} + static struct mtd_info *allocate_partition(struct mtd_info *parent, const struct mtd_partition *part, int partno, uint64_t cur_offset) @@ -309,13 +315,11 @@ static int __mtd_del_partition(struct mtd_info *mtd) sysfs_remove_files(&mtd->dev.kobj, mtd_partition_attrs); + list_del_init(&mtd->part.node); err = del_mtd_device(mtd); if (err) return err; - list_del(&mtd->part.node); - free_partition(mtd); - return 0; } @@ -334,6 +338,7 @@ static int __del_mtd_partitions(struct mtd_info *mtd) __del_mtd_partitions(child); pr_info("Deleting %s MTD partition\n", child->name); + list_del_init(&child->part.node); ret = del_mtd_device(child); if (ret < 0) { pr_err("Error when deleting partition \"%s\" (%d)\n", @@ -341,9 +346,6 @@ static int __del_mtd_partitions(struct mtd_info *mtd) err = ret; continue; } - - list_del(&child->part.node); - free_partition(child); } return err; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 7c58c44662b8..914a9f974baa 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -379,7 +379,7 @@ struct mtd_info { struct module *owner; struct device dev; - int usecount; + struct kref refcnt; struct mtd_debug_info dbg; struct nvmem_device *nvmem; struct nvmem_device *otp_user_nvmem; From 1cc0c0baa361c53ed4b7095495562ca313491d28 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Mon, 31 Jul 2023 14:58:36 +0300 Subject: [PATCH 14/17] UPSTREAM: mtd: fix use-after-free in mtd release I case of partition device_unregister() in mtd_device_release() calls mtd_release() which frees mtd_info structure for partition. All code after device_unregister in mtd_device_release thus uses already freed memory. Move part of code to mtd_release() and restict mtd->dev cleanup to non-partion object. For partition object such cleanup have no sense as partition mtd_info is removed. Cc: Miquel Raynal Cc: Zhang Xiaoxu Fixes: 19bfa9ebebb5 ("mtd: use refcount to prevent corruption") Reviewed-by: Tomas Winkler Signed-off-by: Alexander Usyskin Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230731115836.542747-1-alexander.usyskin@intel.com Signed-off-by: Jon Lin (cherry picked from commit e9714c22c1a8238a85d069b1517941fc723312f7) Change-Id: Ie124021921b2f5efcb131b5c4860e19efaf5228a --- drivers/mtd/mtdcore.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 565b948c99dd..5ff74240ec7b 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -92,6 +92,9 @@ static void mtd_release(struct device *dev) struct mtd_info *mtd = dev_get_drvdata(dev); dev_t index = MTD_DEVT(mtd->index); + idr_remove(&mtd_idr, mtd->index); + of_node_put(mtd_get_of_node(mtd)); + if (mtd_is_partition(mtd)) release_mtd_partition(mtd); @@ -102,6 +105,7 @@ static void mtd_release(struct device *dev) static void mtd_device_release(struct kref *kref) { struct mtd_info *mtd = container_of(kref, struct mtd_info, refcnt); + bool is_partition = mtd_is_partition(mtd); debugfs_remove_recursive(mtd->dbg.dfs_dir); @@ -110,11 +114,13 @@ static void mtd_device_release(struct kref *kref) device_unregister(&mtd->dev); - /* Clear dev so mtd can be safely re-registered later if desired */ - memset(&mtd->dev, 0, sizeof(mtd->dev)); - - idr_remove(&mtd_idr, mtd->index); - of_node_put(mtd_get_of_node(mtd)); + /* + * Clear dev so mtd can be safely re-registered later if desired. + * Should not be done for partition, + * as it was already destroyed in device_unregister(). + */ + if (!is_partition) + memset(&mtd->dev, 0, sizeof(mtd->dev)); module_put(THIS_MODULE); } From 6c3662a2c5d933e390dc6b4a39030b50b483bb5f Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 20 Jun 2023 16:19:05 +0300 Subject: [PATCH 15/17] UPSTREAM: mtd: call external _get and _put in right order MTD provider provides mtd_info object to mtd subsystem. With kref patch the mtd_info object can be alive after provider released mtd device. Fix calling order in _get and _put functions to allow mtd provider to safely alloc and release mtd object. Execute: 1) call external _get 2) get_module 3) add internal kref in the get function and opposite order in the put one. The _put_device callback should be the last in put as the master struct memory may be freed in this callback. Signed-off-by: Alexander Usyskin Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230620131905.648089-3-alexander.usyskin@intel.com Signed-off-by: Jon Lin (cherry picked from commit 79c4a56250216991f1d965ee26dcd273376e4e91) Change-Id: I12b1b5f3ac0bcc432cee429c5c56a057e0a2e589 --- drivers/mtd/mtdcore.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 5ff74240ec7b..2ea9337a7943 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1215,21 +1215,20 @@ int __get_mtd_device(struct mtd_info *mtd) struct mtd_info *master = mtd_get_master(mtd); int err; - if (!try_module_get(master->owner)) - return -ENODEV; - - kref_get(&mtd->refcnt); - if (master->_get_device) { err = master->_get_device(mtd); - - if (err) { - kref_put(&mtd->refcnt, mtd_device_release); - module_put(master->owner); + if (err) return err; - } } + if (!try_module_get(master->owner)) { + if (master->_put_device) + master->_put_device(master); + return -ENODEV; + } + + kref_get(&mtd->refcnt); + while (mtd->parent) { if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) || mtd->parent != master) kref_get(&mtd->parent->refcnt); @@ -1326,13 +1325,14 @@ void __put_mtd_device(struct mtd_info *mtd) mtd = parent; } - if (master->_put_device) - master->_put_device(master); + if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) + kref_put(&master->refcnt, mtd_device_release); module_put(master->owner); - if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) - kref_put(&master->refcnt, mtd_device_release); + /* must be the last as master can be freed in the _put_device */ + if (master->_put_device) + master->_put_device(master); } EXPORT_SYMBOL_GPL(__put_mtd_device); From 6f42afbf707e2cde400c1932896e6a38363202f2 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 31 Jul 2023 11:09:03 +0200 Subject: [PATCH 16/17] UPSTREAM: mtd: Clean refcounting with MTD_PARTITIONED_MASTER The logic is way too convoluted, let's clean the kref_get/put section to clarify what this block does when using CONFIG_MTD_PARTITIONED_MASTER: - Iterate through all the parent mtd devices - Grab a reference over them all but the master - Only grab the master whith CONFIG_MTD_PARTITIONED_MASTER Same logic must apply in the put path, otherwise it would be broken. Cc: Tomas Winkler Cc: Alexander Usyskin Cc: Zhang Xiaoxu Fixes: 19bfa9ebebb5 ("mtd: use refcount to prevent corruption") Signed-off-by: Miquel Raynal Tested-by: Alexander Usyskin Link: https://lore.kernel.org/linux-mtd/20230731090903.770277-1-miquel.raynal@bootlin.com Signed-off-by: Jon Lin (cherry picked from commit 264725e35fbc3b67e053a405e022393a6017e6da) Change-Id: Iff8dd692dda07361a1c4bc9db73908dfd8256419 --- drivers/mtd/mtdcore.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 2ea9337a7943..6f887492c1b9 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1227,14 +1227,15 @@ int __get_mtd_device(struct mtd_info *mtd) return -ENODEV; } - kref_get(&mtd->refcnt); - - while (mtd->parent) { - if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) || mtd->parent != master) - kref_get(&mtd->parent->refcnt); + while (mtd) { + if (mtd != master) + kref_get(&mtd->refcnt); mtd = mtd->parent; } + if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) + kref_get(&master->refcnt); + return 0; } EXPORT_SYMBOL_GPL(__get_mtd_device); @@ -1318,10 +1319,12 @@ void __put_mtd_device(struct mtd_info *mtd) { struct mtd_info *master = mtd_get_master(mtd); - while (mtd != master) { + while (mtd) { + /* kref_put() can relese mtd, so keep a reference mtd->parent */ struct mtd_info *parent = mtd->parent; - kref_put(&mtd->refcnt, mtd_device_release); + if (mtd != master) + kref_put(&mtd->refcnt, mtd_device_release); mtd = parent; } From 95a90239c174bfb41a9d21b265a8b6df0eafc089 Mon Sep 17 00:00:00 2001 From: Zhibin Huang Date: Fri, 27 Dec 2024 11:50:28 +0800 Subject: [PATCH 17/17] misc: rk628: combtxphy: enabling ssc is required for frac_div Type: Fix Redmine ID: #525257 Associated modifications: N/A Test: N/A Signed-off-by: Zhibin Huang Change-Id: Ic107c98c9b9a587699b352358715dfed59e97085 --- drivers/misc/rk628/rk628_combtxphy.c | 49 +++++++++++++++++++--------- drivers/misc/rk628/rk628_combtxphy.h | 4 +++ 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/drivers/misc/rk628/rk628_combtxphy.c b/drivers/misc/rk628/rk628_combtxphy.c index 2cc73482d16f..bd131db59afc 100644 --- a/drivers/misc/rk628/rk628_combtxphy.c +++ b/drivers/misc/rk628/rk628_combtxphy.c @@ -27,6 +27,12 @@ static void rk628_combtxphy_dsi_power_on(struct rk628 *rk628) rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_MODULEB_EN_MASK, SW_MODULEB_EN); + rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_PD_PLL, SW_PD_PLL); + + if (combtxphy->frac_div) + rk628_i2c_update_bits(rk628, COMBTXPHY_CON8, SW_SSC_DEPTH_MASK | SW_SSC_EN_MASK, + SW_SSC_DEPTH(0) | SW_SSC_EN(1)); + rk628_i2c_write(rk628, COMBTXPHY_CON5, SW_REF_DIV(combtxphy->ref_div - 1) | SW_PLL_FB_DIV(combtxphy->fb_div) | @@ -201,7 +207,7 @@ void rk628_combtxphy_set_gvi_division_mode(struct rk628 *rk628, bool division) void rk628_combtxphy_set_mode(struct rk628 *rk628, enum phy_mode mode) { struct rk628_combtxphy *combtxphy = &rk628->combtxphy; - unsigned int fvco, fpfd, frac_rate, fin = 24; + unsigned int fvco, fpfd, fin = 24; switch (mode) { case PHY_MODE_VIDEO_MIPI: @@ -210,7 +216,14 @@ void rk628_combtxphy_set_mode(struct rk628 *rk628, enum phy_mode mode) unsigned int fhsc = bus_width >> 8; unsigned int flags = bus_width & 0xff; - fhsc = fin * (fhsc / fin); + combtxphy->flags = flags; + + /* + * the VCO can work from 1.5GHz to 3GHz. + * fhsc: 80 ~ 374, fvco: 640 ~ 2992 + * fhsc: 375 ~ 749, fvco: 1500 ~ 2996 + * fhsc: 750 ~ 1500, fvco: 1500 ~ 3000 + */ if (fhsc < 80 || fhsc > 1500) return; else if (fhsc < 375) @@ -220,22 +233,26 @@ void rk628_combtxphy_set_mode(struct rk628 *rk628, enum phy_mode mode) else combtxphy->rate_div = 1; - combtxphy->flags = flags; - fvco = fhsc * 2 * combtxphy->rate_div; - combtxphy->ref_div = 1; - combtxphy->fb_div = fvco / 8 / fin; - frac_rate = fvco - (fin * 8 * combtxphy->fb_div); - if (frac_rate) { - frac_rate <<= 10; - frac_rate /= fin * 8; - combtxphy->frac_div = frac_rate; - } else { - combtxphy->frac_div = 0; - } - fvco = fin * (1024 * combtxphy->fb_div + combtxphy->frac_div); - fvco *= 8; + /* + * the reference clock at PFD(FPFD = ref_clk / ref_div) about + * 25MHz is recommende, FPFD must range from 16MHz to 35MHz, + * here to find the best ref_div. + */ + combtxphy->ref_div = 1; + + /* + * fvco = fin * (fb_div + frac_div / 1024) * 8 / ref_div + */ + combtxphy->fb_div = fvco * combtxphy->ref_div / 8 / fin; + combtxphy->frac_div = 1024 * fvco * combtxphy->ref_div / 8 / fin; + combtxphy->frac_div -= 1024 * combtxphy->fb_div; + + /* + * get the actually frequency + */ + fvco = fin * (1024 * combtxphy->fb_div + combtxphy->frac_div) * 8; fvco = DIV_ROUND_UP(fvco, 1024 * combtxphy->ref_div); fhsc = fvco / 2 / combtxphy->rate_div; combtxphy->bus_width = fhsc; diff --git a/drivers/misc/rk628/rk628_combtxphy.h b/drivers/misc/rk628/rk628_combtxphy.h index efc91b9e010e..0174f5160d1c 100644 --- a/drivers/misc/rk628/rk628_combtxphy.h +++ b/drivers/misc/rk628/rk628_combtxphy.h @@ -58,6 +58,10 @@ #define TX_COM_VOLT_ADJ(x) UPDATE(x, 2, 0) #define COMBTXPHY_CON8 REG(0x0020) +#define SW_SSC_DEPTH_MASK GENMASK(7, 4) +#define SW_SSC_DEPTH(x) UPDATE(x, 7, 4) +#define SW_SSC_EN_MASK BIT(0) +#define SW_SSC_EN(x) UPDATE(x, 0, 0) #define COMBTXPHY_CON9 REG(0x0024) #define SW_DSI_FSET_EN_MASK BIT(29) #define SW_DSI_FSET_EN BIT(29)