From afef20585abe55c44d376f3cacf435ddde149d77 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 29 Dec 2020 14:58:58 +0100 Subject: phy: dphy: Add configuration helpers The MIPI D-PHY spec defines default values and boundaries for most of the parameters it defines. Introduce helpers to help drivers get meaningful values based on their current parameters, and validate the boundaries of these parameters if needed. These helpers and header are taken from Linux commit 9123e3a74ec7 ("Linux 5.9-rc1"). Signed-off-by: Neil Armstrong --- drivers/phy/Kconfig | 5 + drivers/phy/Makefile | 1 + drivers/phy/phy-core-mipi-dphy.c | 161 ++++++++++++++++++++++ include/phy-mipi-dphy.h | 284 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 451 insertions(+) create mode 100644 drivers/phy/phy-core-mipi-dphy.c create mode 100644 include/phy-mipi-dphy.h diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index ab638f0..425737c 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -59,6 +59,11 @@ config SPL_NOP_PHY This is useful when a driver uses the PHY framework but no real PHY hardware exists. +config MIPI_DPHY_HELPERS + bool "MIPI D-PHY support helpers" + help + Provides a number of helpers a core functions for MIPI D-PHY drivers. + config BCM6318_USBH_PHY bool "BCM6318 USBH PHY support" depends on PHY && ARCH_BMIPS diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 6b3761b..7b81ff7 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_$(SPL_)PHY) += phy-uclass.o obj-$(CONFIG_$(SPL_)NOP_PHY) += nop-phy.o +obj-$(CONFIG_MIPI_DPHY_HELPERS) += phy-core-mipi-dphy.o obj-$(CONFIG_BCM6318_USBH_PHY) += bcm6318-usbh-phy.o obj-$(CONFIG_BCM6348_USBH_PHY) += bcm6348-usbh-phy.o obj-$(CONFIG_BCM6358_USBH_PHY) += bcm6358-usbh-phy.o diff --git a/drivers/phy/phy-core-mipi-dphy.c b/drivers/phy/phy-core-mipi-dphy.c new file mode 100644 index 0000000..ba5f648 --- /dev/null +++ b/drivers/phy/phy-core-mipi-dphy.c @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2013 NVIDIA Corporation + * Copyright (C) 2018 Cadence Design Systems Inc. + */ + +#include +#include + +#include + +#define PSEC_PER_SEC 1000000000000LL + +/* + * Minimum D-PHY timings based on MIPI D-PHY specification. Derived + * from the valid ranges specified in Section 6.9, Table 14, Page 41 + * of the D-PHY specification (v2.1). + */ +int phy_mipi_dphy_get_default_config(unsigned long pixel_clock, + unsigned int bpp, + unsigned int lanes, + struct phy_configure_opts_mipi_dphy *cfg) +{ + unsigned long long hs_clk_rate; + unsigned long long ui; + + if (!cfg) + return -EINVAL; + + hs_clk_rate = pixel_clock * bpp; + do_div(hs_clk_rate, lanes); + + ui = ALIGN(PSEC_PER_SEC, hs_clk_rate); + do_div(ui, hs_clk_rate); + + cfg->clk_miss = 0; + cfg->clk_post = 60000 + 52 * ui; + cfg->clk_pre = 8000; + cfg->clk_prepare = 38000; + cfg->clk_settle = 95000; + cfg->clk_term_en = 0; + cfg->clk_trail = 60000; + cfg->clk_zero = 262000; + cfg->d_term_en = 0; + cfg->eot = 0; + cfg->hs_exit = 100000; + cfg->hs_prepare = 40000 + 4 * ui; + cfg->hs_zero = 105000 + 6 * ui; + cfg->hs_settle = 85000 + 6 * ui; + cfg->hs_skip = 40000; + + /* + * The MIPI D-PHY specification (Section 6.9, v1.2, Table 14, Page 40) + * contains this formula as: + * + * T_HS-TRAIL = max(n * 8 * ui, 60 + n * 4 * ui) + * + * where n = 1 for forward-direction HS mode and n = 4 for reverse- + * direction HS mode. There's only one setting and this function does + * not parameterize on anything other that ui, so this code will + * assumes that reverse-direction HS mode is supported and uses n = 4. + */ + cfg->hs_trail = max(4 * 8 * ui, 60000 + 4 * 4 * ui); + + cfg->init = 100; + cfg->lpx = 60000; + cfg->ta_get = 5 * cfg->lpx; + cfg->ta_go = 4 * cfg->lpx; + cfg->ta_sure = 2 * cfg->lpx; + cfg->wakeup = 1000; + + cfg->hs_clk_rate = hs_clk_rate; + cfg->lanes = lanes; + + return 0; +} + +/* + * Validate D-PHY configuration according to MIPI D-PHY specification + * (v1.2, Section Section 6.9 "Global Operation Timing Parameters"). + */ +int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg) +{ + unsigned long long ui; + + if (!cfg) + return -EINVAL; + + ui = ALIGN(PSEC_PER_SEC, cfg->hs_clk_rate); + do_div(ui, cfg->hs_clk_rate); + + if (cfg->clk_miss > 60000) + return -EINVAL; + + if (cfg->clk_post < (60000 + 52 * ui)) + return -EINVAL; + + if (cfg->clk_pre < 8000) + return -EINVAL; + + if (cfg->clk_prepare < 38000 || cfg->clk_prepare > 95000) + return -EINVAL; + + if (cfg->clk_settle < 95000 || cfg->clk_settle > 300000) + return -EINVAL; + + if (cfg->clk_term_en > 38000) + return -EINVAL; + + if (cfg->clk_trail < 60000) + return -EINVAL; + + if ((cfg->clk_prepare + cfg->clk_zero) < 300000) + return -EINVAL; + + if (cfg->d_term_en > (35000 + 4 * ui)) + return -EINVAL; + + if (cfg->eot > (105000 + 12 * ui)) + return -EINVAL; + + if (cfg->hs_exit < 100000) + return -EINVAL; + + if (cfg->hs_prepare < (40000 + 4 * ui) || + cfg->hs_prepare > (85000 + 6 * ui)) + return -EINVAL; + + if ((cfg->hs_prepare + cfg->hs_zero) < (145000 + 10 * ui)) + return -EINVAL; + + if ((cfg->hs_settle < (85000 + 6 * ui)) || + (cfg->hs_settle > (145000 + 10 * ui))) + return -EINVAL; + + if (cfg->hs_skip < 40000 || cfg->hs_skip > (55000 + 4 * ui)) + return -EINVAL; + + if (cfg->hs_trail < max(8 * ui, 60000 + 4 * ui)) + return -EINVAL; + + if (cfg->init < 100) + return -EINVAL; + + if (cfg->lpx < 50000) + return -EINVAL; + + if (cfg->ta_get != (5 * cfg->lpx)) + return -EINVAL; + + if (cfg->ta_go != (4 * cfg->lpx)) + return -EINVAL; + + if (cfg->ta_sure < cfg->lpx || cfg->ta_sure > (2 * cfg->lpx)) + return -EINVAL; + + if (cfg->wakeup < 1000) + return -EINVAL; + + return 0; +} diff --git a/include/phy-mipi-dphy.h b/include/phy-mipi-dphy.h new file mode 100644 index 0000000..a877ffe --- /dev/null +++ b/include/phy-mipi-dphy.h @@ -0,0 +1,284 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Cadence Design Systems Inc. + */ + +#ifndef __PHY_MIPI_DPHY_H_ +#define __PHY_MIPI_DPHY_H_ + +/** + * struct phy_configure_opts_mipi_dphy - MIPI D-PHY configuration set + * + * This structure is used to represent the configuration state of a + * MIPI D-PHY phy. + */ +struct phy_configure_opts_mipi_dphy { + /** + * @clk_miss: + * + * Timeout, in picoseconds, for receiver to detect absence of + * Clock transitions and disable the Clock Lane HS-RX. + * + * Maximum value: 60000 ps + */ + unsigned int clk_miss; + + /** + * @clk_post: + * + * Time, in picoseconds, that the transmitter continues to + * send HS clock after the last associated Data Lane has + * transitioned to LP Mode. Interval is defined as the period + * from the end of @hs_trail to the beginning of @clk_trail. + * + * Minimum value: 60000 ps + 52 * @hs_clk_rate period in ps + */ + unsigned int clk_post; + + /** + * @clk_pre: + * + * Time, in UI, that the HS clock shall be driven by + * the transmitter prior to any associated Data Lane beginning + * the transition from LP to HS mode. + * + * Minimum value: 8 UI + */ + unsigned int clk_pre; + + /** + * @clk_prepare: + * + * Time, in picoseconds, that the transmitter drives the Clock + * Lane LP-00 Line state immediately before the HS-0 Line + * state starting the HS transmission. + * + * Minimum value: 38000 ps + * Maximum value: 95000 ps + */ + unsigned int clk_prepare; + + /** + * @clk_settle: + * + * Time interval, in picoseconds, during which the HS receiver + * should ignore any Clock Lane HS transitions, starting from + * the beginning of @clk_prepare. + * + * Minimum value: 95000 ps + * Maximum value: 300000 ps + */ + unsigned int clk_settle; + + /** + * @clk_term_en: + * + * Time, in picoseconds, for the Clock Lane receiver to enable + * the HS line termination. + * + * Maximum value: 38000 ps + */ + unsigned int clk_term_en; + + /** + * @clk_trail: + * + * Time, in picoseconds, that the transmitter drives the HS-0 + * state after the last payload clock bit of a HS transmission + * burst. + * + * Minimum value: 60000 ps + */ + unsigned int clk_trail; + + /** + * @clk_zero: + * + * Time, in picoseconds, that the transmitter drives the HS-0 + * state prior to starting the Clock. + */ + unsigned int clk_zero; + + /** + * @d_term_en: + * + * Time, in picoseconds, for the Data Lane receiver to enable + * the HS line termination. + * + * Maximum value: 35000 ps + 4 * @hs_clk_rate period in ps + */ + unsigned int d_term_en; + + /** + * @eot: + * + * Transmitted time interval, in picoseconds, from the start + * of @hs_trail or @clk_trail, to the start of the LP- 11 + * state following a HS burst. + * + * Maximum value: 105000 ps + 12 * @hs_clk_rate period in ps + */ + unsigned int eot; + + /** + * @hs_exit: + * + * Time, in picoseconds, that the transmitter drives LP-11 + * following a HS burst. + * + * Minimum value: 100000 ps + */ + unsigned int hs_exit; + + /** + * @hs_prepare: + * + * Time, in picoseconds, that the transmitter drives the Data + * Lane LP-00 Line state immediately before the HS-0 Line + * state starting the HS transmission. + * + * Minimum value: 40000 ps + 4 * @hs_clk_rate period in ps + * Maximum value: 85000 ps + 6 * @hs_clk_rate period in ps + */ + unsigned int hs_prepare; + + /** + * @hs_settle: + * + * Time interval, in picoseconds, during which the HS receiver + * shall ignore any Data Lane HS transitions, starting from + * the beginning of @hs_prepare. + * + * Minimum value: 85000 ps + 6 * @hs_clk_rate period in ps + * Maximum value: 145000 ps + 10 * @hs_clk_rate period in ps + */ + unsigned int hs_settle; + + /** + * @hs_skip: + * + * Time interval, in picoseconds, during which the HS-RX + * should ignore any transitions on the Data Lane, following a + * HS burst. The end point of the interval is defined as the + * beginning of the LP-11 state following the HS burst. + * + * Minimum value: 40000 ps + * Maximum value: 55000 ps + 4 * @hs_clk_rate period in ps + */ + unsigned int hs_skip; + + /** + * @hs_trail: + * + * Time, in picoseconds, that the transmitter drives the + * flipped differential state after last payload data bit of a + * HS transmission burst + * + * Minimum value: max(8 * @hs_clk_rate period in ps, + * 60000 ps + 4 * @hs_clk_rate period in ps) + */ + unsigned int hs_trail; + + /** + * @hs_zero: + * + * Time, in picoseconds, that the transmitter drives the HS-0 + * state prior to transmitting the Sync sequence. + */ + unsigned int hs_zero; + + /** + * @init: + * + * Time, in microseconds for the initialization period to + * complete. + * + * Minimum value: 100 us + */ + unsigned int init; + + /** + * @lpx: + * + * Transmitted length, in picoseconds, of any Low-Power state + * period. + * + * Minimum value: 50000 ps + */ + unsigned int lpx; + + /** + * @ta_get: + * + * Time, in picoseconds, that the new transmitter drives the + * Bridge state (LP-00) after accepting control during a Link + * Turnaround. + * + * Value: 5 * @lpx + */ + unsigned int ta_get; + + /** + * @ta_go: + * + * Time, in picoseconds, that the transmitter drives the + * Bridge state (LP-00) before releasing control during a Link + * Turnaround. + * + * Value: 4 * @lpx + */ + unsigned int ta_go; + + /** + * @ta_sure: + * + * Time, in picoseconds, that the new transmitter waits after + * the LP-10 state before transmitting the Bridge state + * (LP-00) during a Link Turnaround. + * + * Minimum value: @lpx + * Maximum value: 2 * @lpx + */ + unsigned int ta_sure; + + /** + * @wakeup: + * + * Time, in microseconds, that a transmitter drives a Mark-1 + * state prior to a Stop state in order to initiate an exit + * from ULPS. + * + * Minimum value: 1000 us + */ + unsigned int wakeup; + + /** + * @hs_clk_rate: + * + * Clock rate, in Hertz, of the high-speed clock. + */ + unsigned long hs_clk_rate; + + /** + * @lp_clk_rate: + * + * Clock rate, in Hertz, of the low-power clock. + */ + unsigned long lp_clk_rate; + + /** + * @lanes: + * + * Number of active, consecutive, data lanes, starting from + * lane 0, used for the transmissions. + */ + unsigned char lanes; +}; + +int phy_mipi_dphy_get_default_config(unsigned long pixel_clock, + unsigned int bpp, + unsigned int lanes, + struct phy_configure_opts_mipi_dphy *cfg); +int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg); + +#endif /* __PHY_MIPI_DPHY_H_ */ -- cgit v1.1 From f8da8a82c5761890ec8578a19f51508f0ead4a1b Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 29 Dec 2020 14:58:59 +0100 Subject: generic-phy: add configure op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the PHY configure op callback to the generic PHY uclass to permit configuring the PHY. It's useful for MIPI DSI PHYs to setup the link timings. Signed-off-by:Neil Armstrong Signed-off-by: Neil Armstrong --- drivers/phy/phy-uclass.c | 11 +++++++++++ include/generic-phy.h | 23 +++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/drivers/phy/phy-uclass.c b/drivers/phy/phy-uclass.c index ef03e3a..43ffbce 100644 --- a/drivers/phy/phy-uclass.c +++ b/drivers/phy/phy-uclass.c @@ -204,6 +204,17 @@ int generic_phy_power_off(struct phy *phy) return ret; } +int generic_phy_configure(struct phy *phy, void *params) +{ + struct phy_ops const *ops; + + if (!generic_phy_valid(phy)) + return 0; + ops = phy_dev_ops(phy->dev); + + return ops->configure ? ops->configure(phy, params) : 0; +} + int generic_phy_get_bulk(struct udevice *dev, struct phy_bulk *bulk) { int i, ret, count; diff --git a/include/generic-phy.h b/include/generic-phy.h index 5ab34cd..a17d900 100644 --- a/include/generic-phy.h +++ b/include/generic-phy.h @@ -122,6 +122,20 @@ struct phy_ops { * @return 0 if OK, or a negative error code */ int (*power_off)(struct phy *phy); + + /** + * configure - configure a PHY device + * + * @phy: PHY port to be configured + * @params: PHY Parameters, underlying data is specific to the PHY function + * + * During runtime, the PHY may need to be configured for it's main function. + * This function configures the PHY for it's main function following + * power_on/off() after beeing initialized. + * + * @return 0 if OK, or a negative error code + */ + int (*configure)(struct phy *phy, void *params); }; /** @@ -183,6 +197,15 @@ int generic_phy_power_on(struct phy *phy); */ int generic_phy_power_off(struct phy *phy); +/** + * generic_phy_configure() - configure a PHY device + * + * @phy: PHY port to be configured + * @params: PHY Parameters, underlying data is specific to the PHY function + * @return 0 if OK, or a negative error code + */ +int generic_phy_configure(struct phy *phy, void *params); + /** * generic_phy_get_by_index() - Get a PHY device by integer index. -- cgit v1.1 From 7ef19503bacf4681a7c8a3025ccbec0944a4b70b Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 29 Dec 2020 14:59:00 +0100 Subject: phy: Add Amlogic AXG MIPI D-PHY driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Amlogic AXG SoCs embeds a MIPI D-PHY used to communicate with DSI panels. This D-PHY depends on a separate analog PHY. Signed-off-by:Neil Armstrong Signed-off-by: Neil Armstrong --- drivers/phy/Kconfig | 9 + drivers/phy/Makefile | 1 + drivers/phy/meson-axg-mipi-dphy.c | 393 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 403 insertions(+) create mode 100644 drivers/phy/meson-axg-mipi-dphy.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 425737c..95050c1 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -196,6 +196,15 @@ config MESON_G12A_USB_PHY This is the generic phy driver for the Amlogic Meson G12A USB2 and USB3 PHYS. +config MESON_AXG_MIPI_DPHY + bool "Amlogic Meson AXG MIPI D-PHY" + depends on PHY && ARCH_MESON && MESON_AXG + select MIPI_DPHY_HELPERS + imply REGMAP + help + This is the generic phy driver for the Amlogic Meson AXG + MIPI D-PHY. + config MSM8916_USB_PHY bool "Qualcomm MSM8916 USB PHY support" depends on PHY diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 7b81ff7..bd853e7 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_PHY_STM32_USBPHYC) += phy-stm32-usbphyc.o obj-$(CONFIG_MESON_GXBB_USB_PHY) += meson-gxbb-usb2.o obj-$(CONFIG_MESON_GXL_USB_PHY) += meson-gxl-usb2.o obj-$(CONFIG_MESON_G12A_USB_PHY) += meson-g12a-usb2.o meson-g12a-usb3-pcie.o +obj-$(CONFIG_MESON_AXG_MIPI_DPHY) += meson-axg-mipi-dphy.o obj-$(CONFIG_MSM8916_USB_PHY) += msm8916-usbh-phy.o obj-$(CONFIG_OMAP_USB2_PHY) += omap-usb2-phy.o obj-$(CONFIG_KEYSTONE_USB_PHY) += keystone-usb-phy.o diff --git a/drivers/phy/meson-axg-mipi-dphy.c b/drivers/phy/meson-axg-mipi-dphy.c new file mode 100644 index 0000000..8b24697 --- /dev/null +++ b/drivers/phy/meson-axg-mipi-dphy.c @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Meson AXG MIPI DPHY driver + * + * Copyright (C) 2018 Amlogic, Inc. All rights reserved + * Copyright (C) 2020 BayLibre, SAS + * Author: Neil Armstrong + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* [31] soft reset for the phy. + * 1: reset. 0: dessert the reset. + * [30] clock lane soft reset. + * [29] data byte lane 3 soft reset. + * [28] data byte lane 2 soft reset. + * [27] data byte lane 1 soft reset. + * [26] data byte lane 0 soft reset. + * [25] mipi dsi pll clock selection. + * 1: clock from fixed 850Mhz clock source. 0: from VID2 PLL. + * [12] mipi HSbyteclk enable. + * [11] mipi divider clk selection. + * 1: select the mipi DDRCLKHS from clock divider. + * 0: from PLL clock. + * [10] mipi clock divider control. + * 1: /4. 0: /2. + * [9] mipi divider output enable. + * [8] mipi divider counter enable. + * [7] PLL clock enable. + * [5] LPDT data endian. + * 1 = transfer the high bit first. 0 : transfer the low bit first. + * [4] HS data endian. + * [3] force data byte lane in stop mode. + * [2] force data byte lane 0 in receiver mode. + * [1] write 1 to sync the txclkesc input. the internal logic have to + * use txclkesc to decide Txvalid and Txready. + * [0] enalbe the MIPI DPHY TxDDRClk. + */ +#define MIPI_DSI_PHY_CTRL 0x0 + +/* [31] clk lane tx_hs_en control selection. + * 1: from register. 0: use clk lane state machine. + * [30] register bit for clock lane tx_hs_en. + * [29] clk lane tx_lp_en contrl selection. + * 1: from register. 0: from clk lane state machine. + * [28] register bit for clock lane tx_lp_en. + * [27] chan0 tx_hs_en control selection. + * 1: from register. 0: from chan0 state machine. + * [26] register bit for chan0 tx_hs_en. + * [25] chan0 tx_lp_en control selection. + * 1: from register. 0: from chan0 state machine. + * [24] register bit from chan0 tx_lp_en. + * [23] chan0 rx_lp_en control selection. + * 1: from register. 0: from chan0 state machine. + * [22] register bit from chan0 rx_lp_en. + * [21] chan0 contention detection enable control selection. + * 1: from register. 0: from chan0 state machine. + * [20] register bit from chan0 contention dectection enable. + * [19] chan1 tx_hs_en control selection. + * 1: from register. 0: from chan0 state machine. + * [18] register bit for chan1 tx_hs_en. + * [17] chan1 tx_lp_en control selection. + * 1: from register. 0: from chan0 state machine. + * [16] register bit from chan1 tx_lp_en. + * [15] chan2 tx_hs_en control selection. + * 1: from register. 0: from chan0 state machine. + * [14] register bit for chan2 tx_hs_en. + * [13] chan2 tx_lp_en control selection. + * 1: from register. 0: from chan0 state machine. + * [12] register bit from chan2 tx_lp_en. + * [11] chan3 tx_hs_en control selection. + * 1: from register. 0: from chan0 state machine. + * [10] register bit for chan3 tx_hs_en. + * [9] chan3 tx_lp_en control selection. + * 1: from register. 0: from chan0 state machine. + * [8] register bit from chan3 tx_lp_en. + * [4] clk chan power down. this bit is also used as the power down + * of the whole MIPI_DSI_PHY. + * [3] chan3 power down. + * [2] chan2 power down. + * [1] chan1 power down. + * [0] chan0 power down. + */ +#define MIPI_DSI_CHAN_CTRL 0x4 + +/* [24] rx turn watch dog triggered. + * [23] rx esc watchdog triggered. + * [22] mbias ready. + * [21] txclkesc synced and ready. + * [20:17] clk lane state. {mbias_ready, tx_stop, tx_ulps, tx_hs_active} + * [16:13] chan3 state{0, tx_stop, tx_ulps, tx_hs_active} + * [12:9] chan2 state.{0, tx_stop, tx_ulps, tx_hs_active} + * [8:5] chan1 state. {0, tx_stop, tx_ulps, tx_hs_active} + * [4:0] chan0 state. {TX_STOP, tx_ULPS, hs_active, direction, rxulpsesc} + */ +#define MIPI_DSI_CHAN_STS 0x8 + +/* [31:24] TCLK_PREPARE. + * [23:16] TCLK_ZERO. + * [15:8] TCLK_POST. + * [7:0] TCLK_TRAIL. + */ +#define MIPI_DSI_CLK_TIM 0xc + +/* [31:24] THS_PREPARE. + * [23:16] THS_ZERO. + * [15:8] THS_TRAIL. + * [7:0] THS_EXIT. + */ +#define MIPI_DSI_HS_TIM 0x10 + +/* [31:24] tTA_GET. + * [23:16] tTA_GO. + * [15:8] tTA_SURE. + * [7:0] tLPX. + */ +#define MIPI_DSI_LP_TIM 0x14 + +/* wait time to MIPI DIS analog ready. */ +#define MIPI_DSI_ANA_UP_TIM 0x18 + +/* TINIT. */ +#define MIPI_DSI_INIT_TIM 0x1c + +/* TWAKEUP. */ +#define MIPI_DSI_WAKEUP_TIM 0x20 + +/* when in RxULPS check state, after the the logic enable the analog, + * how long we should wait to check the lP state . + */ +#define MIPI_DSI_LPOK_TIM 0x24 + +/* Watchdog for RX low power state no finished. */ +#define MIPI_DSI_LP_WCHDOG 0x28 + +/* tMBIAS, after send power up signals to analog, + * how long we should wait for analog powered up. + */ +#define MIPI_DSI_ANA_CTRL 0x2c + +/* [31:8] reserved for future. + * [7:0] tCLK_PRE. + */ +#define MIPI_DSI_CLK_TIM1 0x30 + +/* watchdog for turn around waiting time. */ +#define MIPI_DSI_TURN_WCHDOG 0x34 + +/* When in RxULPS state, how frequency we should to check + * if the TX side out of ULPS state. + */ +#define MIPI_DSI_ULPS_CHECK 0x38 +#define MIPI_DSI_TEST_CTRL0 0x3c +#define MIPI_DSI_TEST_CTRL1 0x40 + +#define NSEC_PER_MSEC 1000000L + +struct phy_meson_axg_mipi_dphy_priv { + struct regmap *regmap; +#if CONFIG_IS_ENABLED(CLK) + struct clk clk; +#endif + struct reset_ctl reset; + struct phy analog; + struct phy_configure_opts_mipi_dphy config; +}; + +static int phy_meson_axg_mipi_dphy_configure(struct phy *phy, void *params) +{ + struct udevice *dev = phy->dev; + struct phy_meson_axg_mipi_dphy_priv *priv = dev_get_priv(dev); + struct phy_configure_opts_mipi_dphy *config = params; + int ret; + + ret = phy_mipi_dphy_config_validate(config); + if (ret) + return ret; + + ret = generic_phy_configure(&priv->analog, config); + if (ret) + return ret; + + memcpy(&priv->config, config, sizeof(priv->config)); + + return 0; +} + +static int phy_meson_axg_mipi_dphy_power_on(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct phy_meson_axg_mipi_dphy_priv *priv = dev_get_priv(dev); + unsigned long temp; + int ret; + + ret = generic_phy_power_on(&priv->analog); + if (ret) + return ret; + + /* enable phy clock */ + regmap_write(priv->regmap, MIPI_DSI_PHY_CTRL, 0x1); + regmap_write(priv->regmap, MIPI_DSI_PHY_CTRL, + BIT(0) | /* enable the DSI PLL clock . */ + BIT(7) | /* enable pll clock which connected to DDR clock path */ + BIT(8)); /* enable the clock divider counter */ + + /* enable the divider clock out */ + regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(9), BIT(9)); + + /* enable the byte clock generation. */ + regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(12), BIT(12)); + regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(31), BIT(31)); + regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(31), 0); + + /* Calculate lanebyteclk period in ps */ + temp = (1000000 * 100) / (priv->config.hs_clk_rate / 1000); + temp = temp * 8 * 10; + + regmap_write(priv->regmap, MIPI_DSI_CLK_TIM, + DIV_ROUND_UP(priv->config.clk_trail, temp) | + (DIV_ROUND_UP(priv->config.clk_post + + priv->config.hs_trail, temp) << 8) | + (DIV_ROUND_UP(priv->config.clk_zero, temp) << 16) | + (DIV_ROUND_UP(priv->config.clk_prepare, temp) << 24)); + regmap_write(priv->regmap, MIPI_DSI_CLK_TIM1, + DIV_ROUND_UP(priv->config.clk_pre, temp)); + + regmap_write(priv->regmap, MIPI_DSI_HS_TIM, + DIV_ROUND_UP(priv->config.hs_exit, temp) | + (DIV_ROUND_UP(priv->config.hs_trail, temp) << 8) | + (DIV_ROUND_UP(priv->config.hs_zero, temp) << 16) | + (DIV_ROUND_UP(priv->config.hs_prepare, temp) << 24)); + + regmap_write(priv->regmap, MIPI_DSI_LP_TIM, + DIV_ROUND_UP(priv->config.lpx, temp) | + (DIV_ROUND_UP(priv->config.ta_sure, temp) << 8) | + (DIV_ROUND_UP(priv->config.ta_go, temp) << 16) | + (DIV_ROUND_UP(priv->config.ta_get, temp) << 24)); + + regmap_write(priv->regmap, MIPI_DSI_ANA_UP_TIM, 0x0100); + regmap_write(priv->regmap, MIPI_DSI_INIT_TIM, + DIV_ROUND_UP(priv->config.init * NSEC_PER_MSEC, temp)); + regmap_write(priv->regmap, MIPI_DSI_WAKEUP_TIM, + DIV_ROUND_UP(priv->config.wakeup * NSEC_PER_MSEC, temp)); + regmap_write(priv->regmap, MIPI_DSI_LPOK_TIM, 0x7C); + regmap_write(priv->regmap, MIPI_DSI_ULPS_CHECK, 0x927C); + regmap_write(priv->regmap, MIPI_DSI_LP_WCHDOG, 0x1000); + regmap_write(priv->regmap, MIPI_DSI_TURN_WCHDOG, 0x1000); + + /* Powerup the analog circuit */ + switch (priv->config.lanes) { + case 1: + regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0xe); + break; + case 2: + regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0xc); + break; + case 3: + regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0x8); + break; + case 4: + default: + regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0); + break; + } + + /* Trigger a sync active for esc_clk */ + regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(1), BIT(1)); + + return 0; +} + +static int phy_meson_axg_mipi_dphy_power_off(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct phy_meson_axg_mipi_dphy_priv *priv = dev_get_priv(dev); + + regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0xf); + regmap_write(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(31)); + + return generic_phy_power_off(&priv->analog); +} + +static int phy_meson_axg_mipi_dphy_init(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct phy_meson_axg_mipi_dphy_priv *priv = dev_get_priv(dev); + int ret; + + ret = generic_phy_init(&priv->analog); + if (ret) + return ret; + + ret = reset_assert(&priv->reset); + udelay(1); + ret |= reset_deassert(&priv->reset); + if (ret) + return ret; + + return 0; +} + +static int phy_meson_axg_mipi_dphy_exit(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct phy_meson_axg_mipi_dphy_priv *priv = dev_get_priv(dev); + int ret; + + ret = generic_phy_exit(&priv->analog); + if (ret) + return ret; + + return reset_assert(&priv->reset); +} + +struct phy_ops meson_axg_mipi_dphy_ops = { + .init = phy_meson_axg_mipi_dphy_init, + .exit = phy_meson_axg_mipi_dphy_exit, + .power_on = phy_meson_axg_mipi_dphy_power_on, + .power_off = phy_meson_axg_mipi_dphy_power_off, + .configure = phy_meson_axg_mipi_dphy_configure, +}; + +int meson_axg_mipi_dphy_probe(struct udevice *dev) +{ + struct phy_meson_axg_mipi_dphy_priv *priv = dev_get_priv(dev); + int ret; + + ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap); + if (ret) + return ret; + + ret = generic_phy_get_by_index(dev, 0, &priv->analog); + if (ret) + return ret; + + ret = reset_get_by_index(dev, 0, &priv->reset); + if (ret == -ENOTSUPP) + return 0; + else if (ret) + return ret; + + ret = reset_deassert(&priv->reset); + if (ret) { + reset_release_all(&priv->reset, 1); + return ret; + } + +#if CONFIG_IS_ENABLED(CLK) + ret = clk_get_by_index(dev, 0, &priv->clk); + if (ret < 0) + return ret; + + ret = clk_enable(&priv->clk); + if (ret && ret != -ENOSYS && ret != -ENOTSUPP) { + pr_err("failed to enable PHY clock\n"); + clk_free(&priv->clk); + return ret; + } +#endif + + return 0; +} + +static const struct udevice_id meson_axg_mipi_dphy_ids[] = { + { .compatible = "amlogic,axg-mipi-dphy" }, + { } +}; + +U_BOOT_DRIVER(meson_axg_mipi_dphy) = { + .name = "meson_axg_mipi_dphy", + .id = UCLASS_PHY, + .of_match = meson_axg_mipi_dphy_ids, + .probe = meson_axg_mipi_dphy_probe, + .ops = &meson_axg_mipi_dphy_ops, + .priv_auto_alloc_size = sizeof(struct phy_meson_axg_mipi_dphy_priv), +}; -- cgit v1.1 From 4547551aa019698524e809f81f65d5782f62bc1d Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 29 Dec 2020 14:59:01 +0100 Subject: phy: Add Amlogic AXG MIPI PCIe Analog PHY driver The Amlogic AXG MIPI + PCIe Analog PHY provides function for both PCIe and MIPI DSI at the same time, and provides the Analog part of MIPI DSI transmission and Analog part of the PCIe lines. Signed-off-by: Neil Armstrong --- drivers/phy/Kconfig | 9 ++ drivers/phy/Makefile | 1 + drivers/phy/meson-axg-mipi-pcie-analog.c | 233 +++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+) create mode 100644 drivers/phy/meson-axg-mipi-pcie-analog.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 95050c1..008186a 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -205,6 +205,15 @@ config MESON_AXG_MIPI_DPHY This is the generic phy driver for the Amlogic Meson AXG MIPI D-PHY. +config MESON_AXG_MIPI_PCIE_ANALOG_PHY + bool "Amlogic Meson AXG MIPI PCIe Analog PHY" + depends on PHY && ARCH_MESON && MESON_AXG + select MIPI_DPHY_HELPERS + imply REGMAP + help + This is the generic phy driver for the Amlogic Meson AXG + MIPI PCIe Analog PHY. + config MSM8916_USB_PHY bool "Qualcomm MSM8916 USB PHY support" depends on PHY diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index bd853e7..3c4a673 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_MESON_GXBB_USB_PHY) += meson-gxbb-usb2.o obj-$(CONFIG_MESON_GXL_USB_PHY) += meson-gxl-usb2.o obj-$(CONFIG_MESON_G12A_USB_PHY) += meson-g12a-usb2.o meson-g12a-usb3-pcie.o obj-$(CONFIG_MESON_AXG_MIPI_DPHY) += meson-axg-mipi-dphy.o +obj-$(CONFIG_MESON_AXG_MIPI_PCIE_ANALOG_PHY) += meson-axg-mipi-pcie-analog.o obj-$(CONFIG_MSM8916_USB_PHY) += msm8916-usbh-phy.o obj-$(CONFIG_OMAP_USB2_PHY) += omap-usb2-phy.o obj-$(CONFIG_KEYSTONE_USB_PHY) += keystone-usb-phy.o diff --git a/drivers/phy/meson-axg-mipi-pcie-analog.c b/drivers/phy/meson-axg-mipi-pcie-analog.c new file mode 100644 index 0000000..276e600 --- /dev/null +++ b/drivers/phy/meson-axg-mipi-pcie-analog.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Amlogic AXG MIPI + PCIE analog PHY driver + * + * Copyright (C) 2019 Remi Pommarel + * Copyright (C) 2020 BayLibre, SAS + * Author: Neil Armstrong + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define HHI_MIPI_CNTL0 0x00 +#define HHI_MIPI_CNTL0_COMMON_BLOCK GENMASK(31, 28) +#define HHI_MIPI_CNTL0_ENABLE BIT(29) +#define HHI_MIPI_CNTL0_BANDGAP BIT(26) +#define HHI_MIPI_CNTL0_DIF_REF_CTL1 GENMASK(25, 16) +#define HHI_MIPI_CNTL0_DIF_REF_CTL0 GENMASK(15, 0) + +#define HHI_MIPI_CNTL1 0x04 +#define HHI_MIPI_CNTL1_CH0_CML_PDR_EN BIT(12) +#define HHI_MIPI_CNTL1_LP_ABILITY GENMASK(5, 4) +#define HHI_MIPI_CNTL1_LP_RESISTER BIT(3) +#define HHI_MIPI_CNTL1_INPUT_SETTING BIT(2) +#define HHI_MIPI_CNTL1_INPUT_SEL BIT(1) +#define HHI_MIPI_CNTL1_PRBS7_EN BIT(0) + +#define HHI_MIPI_CNTL2 0x08 +#define HHI_MIPI_CNTL2_CH_PU GENMASK(31, 25) +#define HHI_MIPI_CNTL2_CH_CTL GENMASK(24, 19) +#define HHI_MIPI_CNTL2_CH0_DIGDR_EN BIT(18) +#define HHI_MIPI_CNTL2_CH_DIGDR_EN BIT(17) +#define HHI_MIPI_CNTL2_LPULPS_EN BIT(16) +#define HHI_MIPI_CNTL2_CH_EN GENMASK(15, 11) +#define HHI_MIPI_CNTL2_CH0_LP_CTL GENMASK(10, 1) + +#define DSI_LANE_0 (1 << 4) +#define DSI_LANE_1 (1 << 3) +#define DSI_LANE_CLK (1 << 2) +#define DSI_LANE_2 (1 << 1) +#define DSI_LANE_3 (1 << 0) +#define DSI_LANE_MASK (0x1F) + +struct phy_meson_axg_mipi_pcie_analog_priv { + struct regmap *regmap; + struct phy_configure_opts_mipi_dphy config; + bool dsi_configured; + bool dsi_enabled; + bool powered; +}; + +static void phy_bandgap_enable(struct phy_meson_axg_mipi_pcie_analog_priv *priv) +{ + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, + HHI_MIPI_CNTL0_BANDGAP, HHI_MIPI_CNTL0_BANDGAP); + + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, + HHI_MIPI_CNTL0_ENABLE, HHI_MIPI_CNTL0_ENABLE); +} + +static void phy_bandgap_disable(struct phy_meson_axg_mipi_pcie_analog_priv *priv) +{ + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, + HHI_MIPI_CNTL0_BANDGAP, 0); + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, + HHI_MIPI_CNTL0_ENABLE, 0); +} + +static void phy_dsi_analog_enable(struct phy_meson_axg_mipi_pcie_analog_priv *priv) +{ + u32 reg; + + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, + HHI_MIPI_CNTL0_DIF_REF_CTL1, + FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL1, 0x1b8)); + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, + BIT(31), BIT(31)); + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, + HHI_MIPI_CNTL0_DIF_REF_CTL0, + FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL0, 0x8)); + + regmap_write(priv->regmap, HHI_MIPI_CNTL1, 0x001e); + + regmap_write(priv->regmap, HHI_MIPI_CNTL2, + (0x26e0 << 16) | (0x459 << 0)); + + reg = DSI_LANE_CLK; + switch (priv->config.lanes) { + case 4: + reg |= DSI_LANE_3; + fallthrough; + case 3: + reg |= DSI_LANE_2; + fallthrough; + case 2: + reg |= DSI_LANE_1; + fallthrough; + case 1: + reg |= DSI_LANE_0; + break; + default: + reg = 0; + } + + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL2, + HHI_MIPI_CNTL2_CH_EN, + FIELD_PREP(HHI_MIPI_CNTL2_CH_EN, reg)); + + priv->dsi_enabled = true; +} + +static void phy_dsi_analog_disable(struct phy_meson_axg_mipi_pcie_analog_priv *priv) +{ + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, + HHI_MIPI_CNTL0_DIF_REF_CTL1, + FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL1, 0)); + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, BIT(31), 0); + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, + HHI_MIPI_CNTL0_DIF_REF_CTL1, 0); + + regmap_write(priv->regmap, HHI_MIPI_CNTL1, 0x6); + + regmap_write(priv->regmap, HHI_MIPI_CNTL2, 0x00200000); + + priv->dsi_enabled = false; +} + +static int phy_meson_axg_mipi_pcie_analog_configure(struct phy *phy, void *params) +{ + struct udevice *dev = phy->dev; + struct phy_meson_axg_mipi_pcie_analog_priv *priv = dev_get_priv(dev); + struct phy_configure_opts_mipi_dphy *config = params; + int ret; + + ret = phy_mipi_dphy_config_validate(config); + if (ret) + return ret; + + memcpy(&priv->config, config, sizeof(priv->config)); + + priv->dsi_configured = true; + + /* If PHY was already powered on, setup the DSI analog part */ + if (priv->powered) { + /* If reconfiguring, disable & reconfigure */ + if (priv->dsi_enabled) + phy_dsi_analog_disable(priv); + + udelay(100); + + phy_dsi_analog_enable(priv); + } + + return 0; +} + +static int phy_meson_axg_mipi_pcie_analog_power_on(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct phy_meson_axg_mipi_pcie_analog_priv *priv = dev_get_priv(dev); + + phy_bandgap_enable(priv); + + if (priv->dsi_configured) + phy_dsi_analog_enable(priv); + + priv->powered = true; + + return 0; +} + +static int phy_meson_axg_mipi_pcie_analog_power_off(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct phy_meson_axg_mipi_pcie_analog_priv *priv = dev_get_priv(dev); + + phy_bandgap_disable(priv); + + if (priv->dsi_enabled) + phy_dsi_analog_disable(priv); + + priv->powered = false; + + return 0; +} + +struct phy_ops meson_axg_mipi_pcie_analog_ops = { + .power_on = phy_meson_axg_mipi_pcie_analog_power_on, + .power_off = phy_meson_axg_mipi_pcie_analog_power_off, + .configure = phy_meson_axg_mipi_pcie_analog_configure, +}; + +int meson_axg_mipi_pcie_analog_probe(struct udevice *dev) +{ + struct phy_meson_axg_mipi_pcie_analog_priv *priv = dev_get_priv(dev); + + priv->regmap = syscon_node_to_regmap(dev_get_parent(dev)->node); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + return 0; +} + +static const struct udevice_id meson_axg_mipi_pcie_analog_ids[] = { + { .compatible = "amlogic,axg-mipi-pcie-analog-phy" }, + { } +}; + +U_BOOT_DRIVER(meson_axg_mipi_pcie_analog) = { + .name = "meson_axg_mipi_pcie_analog", + .id = UCLASS_PHY, + .of_match = meson_axg_mipi_pcie_analog_ids, + .probe = meson_axg_mipi_pcie_analog_probe, + .ops = &meson_axg_mipi_pcie_analog_ops, + .priv_auto_alloc_size = sizeof(struct phy_meson_axg_mipi_pcie_analog_priv), +}; -- cgit v1.1 From 8bc780106c1399bd263c4283a8747889a613ca5d Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 4 Feb 2021 10:11:45 +0100 Subject: board: amlogic: odroid: add runtime detection of the N2/N2+/C4/HC4 variants Use the ADC channel 1 to check the hardware revision of the board and detect the N2 vs. N2+ and the C4 vs. HC4 variants. Each of them use different dtb file, so adjust fdtfile environment variable to the detected variant. The ADC min/max values for each variant are taken from the vendor code, adjusted to the 12-bit ADC driver operation mode (vendor code use 10-bit mode). Signed-off-by: Marek Szyprowski Reviewed-by: Jaehoon Chung Signed-off-by: Neil Armstrong --- arch/arm/dts/meson-g12b-odroid-n2-u-boot.dtsi | 6 ++ arch/arm/dts/meson-sm1-odroid-c4-u-boot.dtsi | 6 ++ board/amlogic/odroid-n2/odroid-n2.c | 80 +++++++++++++++++++++++++++ configs/odroid-c4_defconfig | 4 +- configs/odroid-n2_defconfig | 4 +- 5 files changed, 98 insertions(+), 2 deletions(-) diff --git a/arch/arm/dts/meson-g12b-odroid-n2-u-boot.dtsi b/arch/arm/dts/meson-g12b-odroid-n2-u-boot.dtsi index 236f246..a92f9e9 100644 --- a/arch/arm/dts/meson-g12b-odroid-n2-u-boot.dtsi +++ b/arch/arm/dts/meson-g12b-odroid-n2-u-boot.dtsi @@ -5,3 +5,9 @@ */ #include "meson-g12-common-u-boot.dtsi" + +/* SARADC is needed for proper board variant detection */ +&saradc { + status = "okay"; + vref-supply = <&vddao_1v8>; +}; diff --git a/arch/arm/dts/meson-sm1-odroid-c4-u-boot.dtsi b/arch/arm/dts/meson-sm1-odroid-c4-u-boot.dtsi index fbcc828..963bf96 100644 --- a/arch/arm/dts/meson-sm1-odroid-c4-u-boot.dtsi +++ b/arch/arm/dts/meson-sm1-odroid-c4-u-boot.dtsi @@ -12,6 +12,12 @@ snps,reset-active-low; }; +/* SARADC is needed for proper board variant detection */ +&saradc { + status = "okay"; + vref-supply = <&vddao_1v8>; +}; + &tflash_vdd { gpio = <&gpio_ao GPIOAO_3 GPIO_OPEN_DRAIN>; }; diff --git a/board/amlogic/odroid-n2/odroid-n2.c b/board/amlogic/odroid-n2/odroid-n2.c index d995543..863975e 100644 --- a/board/amlogic/odroid-n2/odroid-n2.c +++ b/board/amlogic/odroid-n2/odroid-n2.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -19,6 +20,11 @@ #define EFUSE_MAC_SIZE 12 #define MAC_ADDR_LEN 6 +#define ODROID_HW_VS_ADC_CHANNEL 1 + +#define MESON_SOC_ID_G12B 0x29 +#define MESON_SOC_ID_SM1 0x2b + int mmc_get_env_dev(void) { if (meson_get_boot_device() == BOOT_DEVICE_EMMC) @@ -26,6 +32,79 @@ int mmc_get_env_dev(void) return 0; } +/* Variant detection is based on the ADC RAW values for the channel #1 */ +static struct meson_odroid_boards { + unsigned int soc_id; + unsigned int adc_min; + unsigned int adc_max; + char *variant; +} boards[] = { + /* OdroidN2 rev 2018,7,23 */ + { MESON_SOC_ID_G12B, 80 * 4, 90 * 4, "n2" }, + /* OdroidN2 rev 2018,12,6 */ + { MESON_SOC_ID_G12B, 160 * 4, 170 * 4, "n2" }, + /* OdroidN2 rev 2019,1,17 */ + { MESON_SOC_ID_G12B, 245 * 4, 255 * 4, "n2" }, + /* OdroidN2 rev 2019,2,7 */ + { MESON_SOC_ID_G12B, 330 * 4, 350 * 4, "n2" }, + /* OdroidN2plus rev 2019,11,20 */ + { MESON_SOC_ID_G12B, 410 * 4, 430 * 4, "n2_plus" }, + /* OdroidC4 rev 2020,01,29 */ + { MESON_SOC_ID_SM1, 80 * 4, 100 * 4, "c4" }, + /* OdroidHC4 rev 2019,12,10 */ + { MESON_SOC_ID_SM1, 300 * 4, 320 * 4, "hc4" }, + /* OdroidC4 rev 2019,11,29 */ + { MESON_SOC_ID_SM1, 335 * 4, 345 * 4, "c4" }, + /* OdroidHC4 rev 2020,8,7 */ + { MESON_SOC_ID_SM1, 590 * 4, 610 * 4, "hc4" }, +}; + +static void odroid_set_fdtfile(char *soc, char *variant) +{ + char s[128]; + + snprintf(s, sizeof(s), "amlogic/meson-%s-odroid-%s.dtb", soc, variant); + env_set("fdtfile", s); +} + +static int odroid_detect_variant(void) +{ + char *variant = "", *soc = ""; + unsigned int adcval = 0; + int ret, i, soc_id = 0; + + if (of_machine_is_compatible("amlogic,sm1")) { + soc_id = MESON_SOC_ID_SM1; + soc = "sm1"; + } else if (of_machine_is_compatible("amlogic,g12b")) { + soc_id = MESON_SOC_ID_G12B; + soc = "g12b"; + } else { + return -1; + } + + ret = adc_channel_single_shot("adc@9000", ODROID_HW_VS_ADC_CHANNEL, + &adcval); + if (ret) + return ret; + + for (i = 0 ; i < ARRAY_SIZE(boards) ; ++i) { + if (soc_id == boards[i].soc_id && + adcval >= boards[i].adc_min && + adcval < boards[i].adc_max) { + variant = boards[i].variant; + break; + } + } + + printf("Board variant: %s\n", variant); + env_set("variant", variant); + + odroid_set_fdtfile(soc, variant); + + return 0; +} + int misc_init_r(void) { u8 mac_addr[MAC_ADDR_LEN]; @@ -58,5 +137,6 @@ int misc_init_r(void) meson_generate_serial_ethaddr(); } + odroid_detect_variant(); return 0; } diff --git a/configs/odroid-c4_defconfig b/configs/odroid-c4_defconfig index 367d22d..8a9b8b0 100644 --- a/configs/odroid-c4_defconfig +++ b/configs/odroid-c4_defconfig @@ -8,7 +8,7 @@ CONFIG_DM_GPIO=y CONFIG_MESON_G12A=y CONFIG_DEBUG_UART_BASE=0xff803000 CONFIG_DEBUG_UART_CLOCK=24000000 -CONFIG_IDENT_STRING=" odroid-c4" +CONFIG_IDENT_STRING=" odroid-c4/hc4" CONFIG_DEFAULT_DEVICE_TREE="meson-sm1-odroid-c4" CONFIG_DEBUG_UART=y CONFIG_OF_BOARD_SETUP=y @@ -26,6 +26,8 @@ CONFIG_CMD_REGULATOR=y CONFIG_OF_CONTROL=y CONFIG_SYS_RELOC_GD_ENV_ADDR=y CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_ADC=y +CONFIG_SARADC_MESON=y CONFIG_DM_MMC=y CONFIG_MMC_MESON_GX=y CONFIG_PHY_REALTEK=y diff --git a/configs/odroid-n2_defconfig b/configs/odroid-n2_defconfig index 76c60ab..b82bd78 100644 --- a/configs/odroid-n2_defconfig +++ b/configs/odroid-n2_defconfig @@ -8,7 +8,7 @@ CONFIG_DM_GPIO=y CONFIG_MESON_G12A=y CONFIG_DEBUG_UART_BASE=0xff803000 CONFIG_DEBUG_UART_CLOCK=24000000 -CONFIG_IDENT_STRING=" odroid-n2" +CONFIG_IDENT_STRING=" odroid-n2/n2_plus" CONFIG_DEFAULT_DEVICE_TREE="meson-g12b-odroid-n2" CONFIG_DEBUG_UART=y CONFIG_OF_BOARD_SETUP=y @@ -26,6 +26,8 @@ CONFIG_CMD_REGULATOR=y CONFIG_OF_CONTROL=y CONFIG_SYS_RELOC_GD_ENV_ADDR=y CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_ADC=y +CONFIG_SARADC_MESON=y CONFIG_DM_MMC=y CONFIG_MMC_MESON_GX=y CONFIG_PHY_REALTEK=y -- cgit v1.1