From 46eddbc58f0d038df5fb51d6c27a989ba826a620 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 10 Sep 2020 10:48:13 +0200 Subject: usb: dwc3: add Amlogic GXL & GXL DWC3 Glue The USB support was initialy done with a set of PHYs and dwc3-of-simple because the architecture of the USB complex was not understood correctly at the time (and proper documentation was missing...). But with the G12A family, the USB complex was correctly understood and implemented correctly. This adds a proper driver for the glue, based on the G12A one, but with enough changes to require a different driver in U-Boot. Signed-off-by: Neil Armstrong --- drivers/usb/dwc3/Kconfig | 8 + drivers/usb/dwc3/Makefile | 1 + drivers/usb/dwc3/dwc3-meson-gxl.c | 425 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 434 insertions(+) create mode 100644 drivers/usb/dwc3/dwc3-meson-gxl.c (limited to 'drivers') diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index c302486291f..802ee508d99 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -38,6 +38,14 @@ config USB_DWC3_MESON_G12A Select this for Amlogic Meson G12A Platforms. This wrapper supports Host and Peripheral operation modes. +config USB_DWC3_MESON_GXL + bool "Amlogic Meson GXL USB wrapper" + depends on DM_USB && USB_DWC3 && ARCH_MESON + imply PHY + help + Select this for Amlogic Meson GXL and GXM Platforms. + This wrapper supports Host and Peripheral operation modes. + config USB_DWC3_UNIPHIER bool "DesignWare USB3 Host Support on UniPhier Platforms" depends on ARCH_UNIPHIER && USB_XHCI_DWC3 diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 0b652a6f364..6e3e024e97e 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_USB_DWC3_GADGET) += gadget.o ep0.o obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o +obj-$(CONFIG_USB_DWC3_MESON_GXL) += dwc3-meson-gxl.o obj-$(CONFIG_USB_DWC3_GENERIC) += dwc3-generic.o obj-$(CONFIG_USB_DWC3_UNIPHIER) += dwc3-uniphier.o obj-$(CONFIG_USB_DWC3_PHY_OMAP) += ti_usb_phy.o diff --git a/drivers/usb/dwc3/dwc3-meson-gxl.c b/drivers/usb/dwc3/dwc3-meson-gxl.c new file mode 100644 index 00000000000..92ee0866f7e --- /dev/null +++ b/drivers/usb/dwc3/dwc3-meson-gxl.c @@ -0,0 +1,425 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Amlogic GXL DWC3 Glue layer + * + * Copyright (C) 2019 BayLibre, SAS + * Author: Neil Armstrong + */ + +#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "gadget.h" +#include +#include +#include +#include +#include +#include +#include + +/* USB Glue Control Registers */ + +#define USB_R0 0x00 + #define USB_R0_P30_FSEL_MASK GENMASK(5, 0) + #define USB_R0_P30_PHY_RESET BIT(6) + #define USB_R0_P30_TEST_POWERDOWN_HSP BIT(7) + #define USB_R0_P30_TEST_POWERDOWN_SSP BIT(8) + #define USB_R0_P30_ACJT_LEVEL_MASK GENMASK(13, 9) + #define USB_R0_P30_TX_BOOST_LEVEL_MASK GENMASK(16, 14) + #define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17) + #define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18) + #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19) + #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29) + #define USB_R0_U2D_ACT BIT(31) + +#define USB_R1 0x04 + #define USB_R1_U3H_BIGENDIAN_GS BIT(0) + #define USB_R1_U3H_PME_ENABLE BIT(1) + #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(6, 2) + #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(11, 7) + #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(15, 12) + #define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16) + #define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17) + #define USB_R1_U3H_HOST_MSI_ENABLE BIT(18) + #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19) + #define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25) + +#define USB_R2 0x08 + #define USB_R2_P30_CR_DATA_IN_MASK GENMASK(15, 0) + #define USB_R2_P30_CR_READ BIT(16) + #define USB_R2_P30_CR_WRITE BIT(17) + #define USB_R2_P30_CR_CAP_ADDR BIT(18) + #define USB_R2_P30_CR_CAP_DATA BIT(19) + #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20) + #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26) + +#define USB_R3 0x0c + #define USB_R3_P30_SSC_ENABLE BIT(0) + #define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1) + #define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4) + #define USB_R3_P30_REF_SSP_EN BIT(13) + #define USB_R3_P30_LOS_BIAS_MASK GENMASK(18, 16) + #define USB_R3_P30_LOS_LEVEL_MASK GENMASK(23, 19) + #define USB_R3_P30_MPLL_MULTIPLIER_MASK GENMASK(30, 24) + +#define USB_R4 0x10 + #define USB_R4_P21_PORT_RESET_0 BIT(0) + #define USB_R4_P21_SLEEP_M0 BIT(1) + #define USB_R4_MEM_PD_MASK GENMASK(3, 2) + #define USB_R4_P21_ONLY BIT(4) + +#define USB_R5 0x14 + #define USB_R5_ID_DIG_SYNC BIT(0) + #define USB_R5_ID_DIG_REG BIT(1) + #define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2) + #define USB_R5_ID_DIG_EN_0 BIT(4) + #define USB_R5_ID_DIG_EN_1 BIT(5) + #define USB_R5_ID_DIG_CURR BIT(6) + #define USB_R5_ID_DIG_IRQ BIT(7) + #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8) + #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16) + +/* read-only register */ +#define USB_R6 0x18 + #define USB_R6_P30_CR_DATA_OUT_MASK GENMASK(15, 0) + #define USB_R6_P30_CR_ACK BIT(16) + +enum { + USB2_HOST_PHY0 = 0, + USB2_OTG_PHY1, + USB2_HOST_PHY2, + PHY_COUNT, +}; + +static const char *phy_names[PHY_COUNT] = { + "usb2-phy0", "usb2-phy1", "usb2-phy2", +}; + +struct dwc3_meson_gxl { + struct udevice *dev; + struct regmap *regmap; + struct clk clk; + struct reset_ctl reset; + struct phy phys[PHY_COUNT]; + enum usb_dr_mode otg_mode; + enum usb_dr_mode otg_phy_mode; + unsigned int usb2_ports; +#if CONFIG_IS_ENABLED(DM_REGULATOR) + struct udevice *vbus_supply; +#endif +}; + +#define U2P_REG_SIZE 0x20 +#define USB_REG_OFFSET 0x80 + +#define USB2_OTG_PHY USB2_OTG_PHY1 + +static void dwc3_meson_gxl_usb2_set_mode(struct dwc3_meson_gxl *priv, enum usb_dr_mode mode) +{ + switch (mode) { + case USB_DR_MODE_HOST: + case USB_DR_MODE_OTG: + case USB_DR_MODE_UNKNOWN: + regmap_update_bits(priv->regmap, USB_R1, + USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK, 0); + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_ACT, 0); + regmap_update_bits(priv->regmap, USB_R4, + USB_R4_P21_SLEEP_M0, 0); + break; + + case USB_DR_MODE_PERIPHERAL: + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_ACT, USB_R0_U2D_ACT); + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0); + regmap_update_bits(priv->regmap, USB_R4, + USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0); + break; + } +} + +static int dwc3_meson_gxl_usb2_init(struct dwc3_meson_gxl *priv) +{ + int i; + + for (i = 0; i < PHY_COUNT; ++i) { + if (!priv->phys[i].dev) + continue; + + phy_meson_gxl_usb2_set_mode(&priv->phys[i], + (i == USB2_OTG_PHY) ? USB_DR_MODE_PERIPHERAL + : USB_DR_MODE_HOST); + } + + return 0; +} + +static int dwc3_meson_gxl_usb_init(struct dwc3_meson_gxl *priv) +{ + int ret; + + ret = dwc3_meson_gxl_usb2_init(priv); + if (ret) + return ret; + + regmap_update_bits(priv->regmap, USB_R1, + USB_R1_U3H_FLADJ_30MHZ_REG_MASK, + FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20)); + + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_EN_0, + USB_R5_ID_DIG_EN_0); + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_EN_1, + USB_R5_ID_DIG_EN_1); + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_TH_MASK, + FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff)); + + dwc3_meson_gxl_usb2_set_mode(priv, priv->otg_phy_mode); + + return 0; +} + +int dwc3_meson_gxl_force_mode(struct udevice *dev, enum usb_dr_mode mode) +{ + struct dwc3_meson_gxl *priv = dev_get_platdata(dev); + + if (!priv) + return -EINVAL; + + if (mode != USB_DR_MODE_HOST && mode != USB_DR_MODE_PERIPHERAL) + return -EINVAL; + + if (!priv->phys[USB2_OTG_PHY].dev) + return -EINVAL; + + if (mode == priv->otg_phy_mode) + return 0; + + if (mode == USB_DR_MODE_HOST) + debug("%s: switching to Host Mode\n", __func__); + else + debug("%s: switching to Device Mode\n", __func__); + +#if CONFIG_IS_ENABLED(DM_REGULATOR) + if (priv->vbus_supply) { + int ret = regulator_set_enable(priv->vbus_supply, + (mode == USB_DR_MODE_PERIPHERAL)); + if (ret) + return ret; + } +#endif + priv->otg_phy_mode = mode; + + phy_meson_gxl_usb2_set_mode(&priv->phys[USB2_OTG_PHY], mode); + + dwc3_meson_gxl_usb2_set_mode(priv, mode); + + return 0; +} + +static int dwc3_meson_gxl_get_phys(struct dwc3_meson_gxl *priv) +{ + int i, ret; + + for (i = 0 ; i < PHY_COUNT ; ++i) { + ret = generic_phy_get_by_name(priv->dev, phy_names[i], + &priv->phys[i]); + if (ret == -ENOENT || ret == -ENODATA) { + priv->phys[i].dev = NULL; + continue; + } + + if (ret) + return ret; + + priv->usb2_ports++; + } + + debug("%s: usb2 ports: %d\n", __func__, priv->usb2_ports); + + return 0; +} + +static int dwc3_meson_gxl_reset_init(struct dwc3_meson_gxl *priv) +{ + int ret; + + ret = reset_get_by_index(priv->dev, 0, &priv->reset); + if (ret) + return ret; + + ret = reset_assert(&priv->reset); + udelay(1); + ret |= reset_deassert(&priv->reset); + if (ret) { + reset_free(&priv->reset); + return ret; + } + + return 0; +} + +static int dwc3_meson_gxl_clk_init(struct dwc3_meson_gxl *priv) +{ + int ret; + + ret = clk_get_by_index(priv->dev, 0, &priv->clk); + if (ret) + return ret; + +#if CONFIG_IS_ENABLED(CLK) + ret = clk_enable(&priv->clk); + if (ret) { + clk_free(&priv->clk); + return ret; + } +#endif + + return 0; +} + +static int dwc3_meson_gxl_probe(struct udevice *dev) +{ + struct dwc3_meson_gxl *priv = dev_get_platdata(dev); + int ret, i; + + priv->dev = dev; + + ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap); + if (ret) + return ret; + + ret = dwc3_meson_gxl_clk_init(priv); + if (ret) + return ret; + + ret = dwc3_meson_gxl_reset_init(priv); + if (ret) + return ret; + + ret = dwc3_meson_gxl_get_phys(priv); + if (ret) + return ret; + +#if CONFIG_IS_ENABLED(DM_REGULATOR) + ret = device_get_supply_regulator(dev, "vbus-supply", + &priv->vbus_supply); + if (ret && ret != -ENOENT) { + pr_err("Failed to get PHY regulator\n"); + return ret; + } + + if (priv->vbus_supply) { + ret = regulator_set_enable(priv->vbus_supply, true); + if (ret) + return ret; + } +#endif + + /* On GXL PHY must be started in device mode for DWC2 init */ + priv->otg_mode = USB_DR_MODE_PERIPHERAL; + + ret = dwc3_meson_gxl_usb_init(priv); + if (ret) + return ret; + + priv->otg_mode = usb_get_dr_mode(dev->node); + + if (priv->otg_mode == USB_DR_MODE_PERIPHERAL) + priv->otg_phy_mode = USB_DR_MODE_PERIPHERAL; + else + priv->otg_phy_mode = USB_DR_MODE_HOST; + + for (i = 0 ; i < PHY_COUNT ; ++i) { + if (!priv->phys[i].dev) + continue; + + ret = generic_phy_init(&priv->phys[i]); + if (ret) + goto err_phy_init; + } + + for (i = 0; i < PHY_COUNT; ++i) { + if (!priv->phys[i].dev) + continue; + + ret = generic_phy_power_on(&priv->phys[i]); + if (ret) + goto err_phy_init; + } + + if (priv->phys[USB2_OTG_PHY].dev) + phy_meson_gxl_usb2_set_mode(&priv->phys[USB2_OTG_PHY], + priv->otg_phy_mode); + + dwc3_meson_gxl_usb2_set_mode(priv, priv->otg_phy_mode); + + return 0; + +err_phy_init: + for (i = 0 ; i < PHY_COUNT ; ++i) { + if (!priv->phys[i].dev) + continue; + + generic_phy_exit(&priv->phys[i]); + } + + return ret; +} + +static int dwc3_meson_gxl_remove(struct udevice *dev) +{ + struct dwc3_meson_gxl *priv = dev_get_platdata(dev); + int i; + + reset_release_all(&priv->reset, 1); + + clk_release_all(&priv->clk, 1); + + for (i = 0; i < PHY_COUNT; ++i) { + if (!priv->phys[i].dev) + continue; + + generic_phy_power_off(&priv->phys[i]); + } + + for (i = 0 ; i < PHY_COUNT ; ++i) { + if (!priv->phys[i].dev) + continue; + + generic_phy_exit(&priv->phys[i]); + } + + return dm_scan_fdt_dev(dev); +} + +static const struct udevice_id dwc3_meson_gxl_ids[] = { + { .compatible = "amlogic,meson-gxl-usb-ctrl" }, + { .compatible = "amlogic,meson-gxm-usb-ctrl" }, + { } +}; + +U_BOOT_DRIVER(dwc3_generic_wrapper) = { + .name = "dwc3-meson-gxl", + .id = UCLASS_SIMPLE_BUS, + .of_match = dwc3_meson_gxl_ids, + .probe = dwc3_meson_gxl_probe, + .remove = dwc3_meson_gxl_remove, + .platdata_auto_alloc_size = sizeof(struct dwc3_meson_gxl), + +}; -- cgit v1.3.1 From 66d942740307e8199e0671bf667629cb9511436a Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 10 Sep 2020 10:48:15 +0200 Subject: phy: meson-gxl: remove invalid USB3 PHY driver The registers which are managed by the meson-gxl-usb3 PHY driver are actually "USB control" registers (which are "glue" registers which manage OTG detection and routing of the OTG capable port between the DWC2 peripheral-only controller and the DWC3 host-only controller). Drop the meson-gxl-usb3 PHY driver now that the dwc3-meson-gxl-usb driver supports the USB control registers on GXL and GXM SoCs (these were previously managed by the meson-gxl-usb3 PHY driver). Signed-off-by: Neil Armstrong --- arch/arm/include/asm/arch-meson/usb-gx.h | 1 - drivers/phy/Makefile | 2 +- drivers/phy/meson-gxl-usb3.c | 219 ------------------------------- 3 files changed, 1 insertion(+), 221 deletions(-) delete mode 100644 drivers/phy/meson-gxl-usb3.c (limited to 'drivers') diff --git a/arch/arm/include/asm/arch-meson/usb-gx.h b/arch/arm/include/asm/arch-meson/usb-gx.h index 7200a4c31a9..61f1809df9c 100644 --- a/arch/arm/include/asm/arch-meson/usb-gx.h +++ b/arch/arm/include/asm/arch-meson/usb-gx.h @@ -11,7 +11,6 @@ /* TOFIX add set_mode to struct phy_ops */ void phy_meson_gxl_usb2_set_mode(struct phy *phy, enum usb_dr_mode mode); -void phy_meson_gxl_usb3_set_mode(struct phy *phy, enum usb_dr_mode mode); int dwc3_meson_gxl_force_mode(struct udevice *dev, enum usb_dr_mode mode); diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 74e8d931d38..c839c250bd4 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -17,7 +17,7 @@ obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o obj-$(CONFIG_PHY_RCAR_GEN3) += phy-rcar-gen3.o 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 meson-gxl-usb3.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_MSM8916_USB_PHY) += msm8916-usbh-phy.o obj-$(CONFIG_OMAP_USB2_PHY) += omap-usb2-phy.o diff --git a/drivers/phy/meson-gxl-usb3.c b/drivers/phy/meson-gxl-usb3.c deleted file mode 100644 index 9de55bb5df0..00000000000 --- a/drivers/phy/meson-gxl-usb3.c +++ /dev/null @@ -1,219 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Meson GXL USB3 PHY driver - * - * Copyright (C) 2018 Martin Blumenstingl - * Copyright (C) 2018 BayLibre, SAS - * Author: Neil Armstrong - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#define USB_R0 0x00 - #define USB_R0_P30_FSEL_MASK GENMASK(5, 0) - #define USB_R0_P30_PHY_RESET BIT(6) - #define USB_R0_P30_TEST_POWERDOWN_HSP BIT(7) - #define USB_R0_P30_TEST_POWERDOWN_SSP BIT(8) - #define USB_R0_P30_ACJT_LEVEL_MASK GENMASK(13, 9) - #define USB_R0_P30_TX_BOOST_LEVEL_MASK GENMASK(16, 14) - #define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17) - #define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18) - #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19) - #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29) - #define USB_R0_U2D_ACT BIT(31) - -#define USB_R1 0x04 - #define USB_R1_U3H_BIGENDIAN_GS BIT(0) - #define USB_R1_U3H_PME_ENABLE BIT(1) - #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(6, 2) - #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(11, 7) - #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(15, 12) - #define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16) - #define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17) - #define USB_R1_U3H_HOST_MSI_ENABLE BIT(18) - #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19) - #define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25) - -#define USB_R2 0x08 - #define USB_R2_P30_CR_DATA_IN_MASK GENMASK(15, 0) - #define USB_R2_P30_CR_READ BIT(16) - #define USB_R2_P30_CR_WRITE BIT(17) - #define USB_R2_P30_CR_CAP_ADDR BIT(18) - #define USB_R2_P30_CR_CAP_DATA BIT(19) - #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20) - #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26) - -#define USB_R3 0x0c - #define USB_R3_P30_SSC_ENABLE BIT(0) - #define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1) - #define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4) - #define USB_R3_P30_REF_SSP_EN BIT(13) - #define USB_R3_P30_LOS_BIAS_MASK GENMASK(18, 16) - #define USB_R3_P30_LOS_LEVEL_MASK GENMASK(23, 19) - #define USB_R3_P30_MPLL_MULTIPLIER_MASK GENMASK(30, 24) - -#define USB_R4 0x10 - #define USB_R4_P21_PORT_RESET_0 BIT(0) - #define USB_R4_P21_SLEEP_M0 BIT(1) - #define USB_R4_MEM_PD_MASK GENMASK(3, 2) - #define USB_R4_P21_ONLY BIT(4) - -#define USB_R5 0x14 - #define USB_R5_ID_DIG_SYNC BIT(0) - #define USB_R5_ID_DIG_REG BIT(1) - #define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2) - #define USB_R5_ID_DIG_EN_0 BIT(4) - #define USB_R5_ID_DIG_EN_1 BIT(5) - #define USB_R5_ID_DIG_CURR BIT(6) - #define USB_R5_ID_DIG_IRQ BIT(7) - #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8) - #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16) - -/* read-only register */ -#define USB_R6 0x18 - #define USB_R6_P30_CR_DATA_OUT_MASK GENMASK(15, 0) - #define USB_R6_P30_CR_ACK BIT(16) - -struct phy_meson_gxl_usb3_priv { - struct regmap *regmap; -#if CONFIG_IS_ENABLED(CLK) - struct clk clk; -#endif -}; - -void phy_meson_gxl_usb3_set_mode(struct phy *phy, enum usb_dr_mode mode) -{ - struct udevice *dev = phy->dev; - struct phy_meson_gxl_usb3_priv *priv = dev_get_priv(dev); - uint val; - - switch (mode) { - case USB_DR_MODE_UNKNOWN: - case USB_DR_MODE_HOST: - case USB_DR_MODE_OTG: - regmap_read(priv->regmap, USB_R0, &val); - val &= ~USB_R0_U2D_ACT; - regmap_write(priv->regmap, USB_R0, val); - - regmap_read(priv->regmap, USB_R4, &val); - val &= ~USB_R4_P21_SLEEP_M0; - regmap_write(priv->regmap, USB_R4, val); - break; - - case USB_DR_MODE_PERIPHERAL: - regmap_read(priv->regmap, USB_R0, &val); - val |= USB_R0_U2D_ACT; - regmap_write(priv->regmap, USB_R0, val); - - regmap_read(priv->regmap, USB_R4, &val); - val |= USB_R4_P21_SLEEP_M0; - regmap_write(priv->regmap, USB_R4, val); - break; - } -} - -static int phy_meson_gxl_usb3_power_on(struct phy *phy) -{ - struct udevice *dev = phy->dev; - struct phy_meson_gxl_usb3_priv *priv = dev_get_priv(dev); - uint val; - - regmap_read(priv->regmap, USB_R5, &val); - val |= USB_R5_ID_DIG_EN_0; - val |= USB_R5_ID_DIG_EN_1; - val &= ~USB_R5_ID_DIG_TH_MASK; - val |= FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff); - regmap_write(priv->regmap, USB_R5, val); - - phy_meson_gxl_usb3_set_mode(phy, USB_DR_MODE_HOST); - - return 0; -} - -static int phy_meson_gxl_usb3_power_off(struct phy *phy) -{ - struct udevice *dev = phy->dev; - struct phy_meson_gxl_usb3_priv *priv = dev_get_priv(dev); - uint val; - - regmap_read(priv->regmap, USB_R5, &val); - val &= ~USB_R5_ID_DIG_EN_0; - val &= ~USB_R5_ID_DIG_EN_1; - regmap_write(priv->regmap, USB_R5, val); - - return 0; -} - -static int phy_meson_gxl_usb3_init(struct phy *phy) -{ - struct udevice *dev = phy->dev; - struct phy_meson_gxl_usb3_priv *priv = dev_get_priv(dev); - uint val; - - regmap_read(priv->regmap, USB_R1, &val); - val &= ~USB_R1_U3H_FLADJ_30MHZ_REG_MASK; - val |= FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20); - regmap_write(priv->regmap, USB_R1, val); - - return 0; -} - -struct phy_ops meson_gxl_usb3_phy_ops = { - .init = phy_meson_gxl_usb3_init, - .power_on = phy_meson_gxl_usb3_power_on, - .power_off = phy_meson_gxl_usb3_power_off, -}; - -int meson_gxl_usb3_phy_probe(struct udevice *dev) -{ - struct phy_meson_gxl_usb3_priv *priv = dev_get_priv(dev); - int ret; - - ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap); - if (ret) - 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_gxl_usb3_phy_ids[] = { - { .compatible = "amlogic,meson-gxl-usb3-phy" }, - { } -}; - -U_BOOT_DRIVER(meson_gxl_usb3_phy) = { - .name = "meson_gxl_usb3_phy", - .id = UCLASS_PHY, - .of_match = meson_gxl_usb3_phy_ids, - .probe = meson_gxl_usb3_phy_probe, - .ops = &meson_gxl_usb3_phy_ops, - .priv_auto_alloc_size = sizeof(struct phy_meson_gxl_usb3_priv), -}; -- cgit v1.3.1 From 0ae8724d7e64f814fb77d2caef6214fc1bc52991 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 10 Sep 2020 10:48:16 +0200 Subject: phy: meson-gxl-usb: depend on Meson AXG aswell Enable build of meson-gxl-usb PHY for the AXG architecture aswell. Signed-off-by: Neil Armstrong --- drivers/phy/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 9c775107e9c..912e29f7b59 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -164,7 +164,7 @@ config MESON_GXBB_USB_PHY config MESON_GXL_USB_PHY bool "Amlogic Meson GXL USB PHYs" - depends on PHY && ARCH_MESON && (MESON_GXL || MESON_GXM) + depends on PHY && ARCH_MESON && (MESON_GXL || MESON_GXM || MESON_AXG) imply REGMAP help This is the generic phy driver for the Amlogic Meson GXL -- cgit v1.3.1 From 0f6bef4a95672f9497067e90bd2b6441810644f7 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 2 Oct 2020 09:31:46 +0200 Subject: pinctrl: meson-axg-pmx: fix gpio request The AXG pmx driver gpio request offset needs the pin base to have the correct pin number. Signed-off-by: Neil Armstrong Reviewed-by: Mark Kettenis --- drivers/pinctrl/meson/pinctrl-meson-axg-pmx.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/meson/pinctrl-meson-axg-pmx.c b/drivers/pinctrl/meson/pinctrl-meson-axg-pmx.c index c6cb941d0a9..cfe94cf9e17 100644 --- a/drivers/pinctrl/meson/pinctrl-meson-axg-pmx.c +++ b/drivers/pinctrl/meson/pinctrl-meson-axg-pmx.c @@ -165,7 +165,10 @@ const struct pinctrl_ops meson_axg_pinctrl_ops = { static int meson_axg_gpio_request(struct udevice *dev, unsigned int offset, const char *label) { - return meson_axg_pmx_update_function(dev->parent, offset, 0); + struct meson_pinctrl *priv = dev_get_priv(dev->parent); + + return meson_axg_pmx_update_function(dev->parent, + offset + priv->data->pin_base, 0); } static const struct dm_gpio_ops meson_axg_gpio_ops = { -- cgit v1.3.1 From 38c1c6f8163334521def945a379e7fca231f4b62 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 2 Oct 2020 09:32:12 +0200 Subject: pinctrl: meson-axg: add missing GPIOA_18 Add the missing GPIOA_18 from the missing EE gpio list. Signed-off-by: Neil Armstrong --- drivers/pinctrl/meson/pinctrl-meson-axg.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pinctrl/meson/pinctrl-meson-axg.c b/drivers/pinctrl/meson/pinctrl-meson-axg.c index 8f23c8cef1d..11809b29f39 100644 --- a/drivers/pinctrl/meson/pinctrl-meson-axg.c +++ b/drivers/pinctrl/meson/pinctrl-meson-axg.c @@ -298,6 +298,7 @@ static struct meson_pmx_group meson_axg_periphs_groups[] = { GPIO_GROUP(GPIOA_15, EE_OFF), GPIO_GROUP(GPIOA_16, EE_OFF), GPIO_GROUP(GPIOA_17, EE_OFF), + GPIO_GROUP(GPIOA_18, EE_OFF), GPIO_GROUP(GPIOA_19, EE_OFF), GPIO_GROUP(GPIOA_20, EE_OFF), -- cgit v1.3.1 From 2d481b2e3e22f7be854d381a7bafd92a65e18b23 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 1 Oct 2020 10:04:56 +0200 Subject: pwm: Add driver for Amlogic Meson PWM controller This adds the driver for the PWM controller found in the Amlogic SoCs. This PWM is only a set of Gates, Dividers and Counters: PWM output is achieved by calculating a clock that permits calculating two periods (low and high). The counter then has to be set to switch after N cycles for the first half period. The hardware has no "polarity" setting. This driver reverses the period cycles (the low length is inverted with the high length) for PWM_POLARITY_INVERSED. Disabling the PWM stops the output immediately (without waiting for the current period to complete first). Signed-off-by: Neil Armstrong --- drivers/pwm/Kconfig | 7 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-meson.c | 528 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 536 insertions(+) create mode 100644 drivers/pwm/pwm-meson.c (limited to 'drivers') diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 61eb468cdeb..b3bd5c6bb7a 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -23,6 +23,13 @@ config PWM_IMX help This PWM is found i.MX27 and later i.MX SoCs. +config PWM_MESON + bool "Enable support for Amlogic Meson SoCs PWM" + depends on DM_PWM + help + This PWM is found on Amlogic Meson SoCs. It supports a + programmable period and duty cycle for 2 independant channels. + config PWM_MTK bool "Enable support for MediaTek PWM" depends on DM_PWM diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 0f4e84b04de..f21ae7d76ee 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_DM_PWM) += pwm-uclass.o obj-$(CONFIG_PWM_EXYNOS) += exynos_pwm.o obj-$(CONFIG_PWM_IMX) += pwm-imx.o pwm-imx-util.o +obj-$(CONFIG_PWM_MESON) += pwm-meson.o obj-$(CONFIG_PWM_MTK) += pwm-mtk.o obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c new file mode 100644 index 00000000000..cafb571f16f --- /dev/null +++ b/drivers/pwm/pwm-meson.c @@ -0,0 +1,528 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2020 BayLibre, SAS. + * Author: Neil Armstrong + * Copyright (C) 2014 Amlogic, Inc. + * + * This PWM is only a set of Gates, Dividers and Counters: + * PWM output is achieved by calculating a clock that permits calculating + * two periods (low and high). The counter then has to be set to switch after + * N cycles for the first half period. + * The hardware has no "polarity" setting. This driver reverses the period + * cycles (the low length is inverted with the high length) for + * PWM_POLARITY_INVERSED. + * Setting the polarity will disable and re-enable the PWM output. + * Disabling the PWM stops the output immediately (without waiting for the + * current period to complete first). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NSEC_PER_SEC 1000000000L + +#define REG_PWM_A 0x0 +#define REG_PWM_B 0x4 +#define PWM_LOW_MASK GENMASK(15, 0) +#define PWM_HIGH_MASK GENMASK(31, 16) + +#define REG_MISC_AB 0x8 +#define MISC_B_CLK_EN BIT(23) +#define MISC_A_CLK_EN BIT(15) +#define MISC_CLK_DIV_MASK 0x7f +#define MISC_B_CLK_DIV_SHIFT 16 +#define MISC_A_CLK_DIV_SHIFT 8 +#define MISC_B_CLK_SEL_SHIFT 6 +#define MISC_A_CLK_SEL_SHIFT 4 +#define MISC_CLK_SEL_MASK 0x3 +#define MISC_B_EN BIT(1) +#define MISC_A_EN BIT(0) + +#define MESON_NUM_PWMS 2 + +static struct meson_pwm_channel_data { + u8 reg_offset; + u8 clk_sel_shift; + u8 clk_div_shift; + u32 clk_en_mask; + u32 pwm_en_mask; +} meson_pwm_per_channel_data[MESON_NUM_PWMS] = { + { + .reg_offset = REG_PWM_A, + .clk_sel_shift = MISC_A_CLK_SEL_SHIFT, + .clk_div_shift = MISC_A_CLK_DIV_SHIFT, + .clk_en_mask = MISC_A_CLK_EN, + .pwm_en_mask = MISC_A_EN, + }, + { + .reg_offset = REG_PWM_B, + .clk_sel_shift = MISC_B_CLK_SEL_SHIFT, + .clk_div_shift = MISC_B_CLK_DIV_SHIFT, + .clk_en_mask = MISC_B_CLK_EN, + .pwm_en_mask = MISC_B_EN, + } +}; + +struct meson_pwm_channel { + unsigned int hi; + unsigned int lo; + u8 pre_div; + uint period_ns; + uint duty_ns; + bool configured; + bool enabled; + bool polarity; + struct clk clk; +}; + +struct meson_pwm_data { + const long *parent_ids; + unsigned int num_parents; +}; + +struct meson_pwm { + const struct meson_pwm_data *data; + struct meson_pwm_channel channels[MESON_NUM_PWMS]; + void __iomem *base; +}; + +static int meson_pwm_set_enable(struct udevice *dev, uint channel, bool enable); + +static int meson_pwm_set_config(struct udevice *dev, uint channeln, + uint period_ns, uint duty_ns) +{ + struct meson_pwm *priv = dev_get_priv(dev); + struct meson_pwm_channel *channel; + struct meson_pwm_channel_data *channel_data; + unsigned int duty, period, pre_div, cnt, duty_cnt; + unsigned long fin_freq; + + if (channeln >= MESON_NUM_PWMS) + return -ENODEV; + + channel = &priv->channels[channeln]; + channel_data = &meson_pwm_per_channel_data[channeln]; + + period = period_ns; + if (channel->polarity) + duty = period_ns - duty_ns; + else + duty = duty_ns; + + debug("%s%d: polarity %s duty %d period %d\n", __func__, channeln, + channel->polarity ? "true" : "false", duty, period); + + fin_freq = clk_get_rate(&channel->clk); + if (fin_freq == 0) { + printf("%s%d: invalid source clock frequency\n", __func__, channeln); + return -EINVAL; + } + + debug("%s%d: fin_freq: %lu Hz\n", __func__, channeln, fin_freq); + + pre_div = div64_u64(fin_freq * (u64)period, NSEC_PER_SEC * 0xffffLL); + if (pre_div > MISC_CLK_DIV_MASK) { + printf("%s%d: unable to get period pre_div\n", __func__, channeln); + return -EINVAL; + } + + cnt = div64_u64(fin_freq * (u64)period, NSEC_PER_SEC * (pre_div + 1)); + if (cnt > 0xffff) { + printf("%s%d: unable to get period cnt\n", __func__, channeln); + return -EINVAL; + } + + debug("%s%d: period=%u pre_div=%u cnt=%u\n", __func__, channeln, period, pre_div, cnt); + + if (duty == period) { + channel->pre_div = pre_div; + channel->hi = cnt; + channel->lo = 0; + } else if (duty == 0) { + channel->pre_div = pre_div; + channel->hi = 0; + channel->lo = cnt; + } else { + /* Then check is we can have the duty with the same pre_div */ + duty_cnt = div64_u64(fin_freq * (u64)duty, NSEC_PER_SEC * (pre_div + 1)); + if (duty_cnt > 0xffff) { + printf("%s%d: unable to get duty cycle\n", __func__, channeln); + return -EINVAL; + } + + debug("%s%d: duty=%u pre_div=%u duty_cnt=%u\n", + __func__, channeln, duty, pre_div, duty_cnt); + + channel->pre_div = pre_div; + channel->hi = duty_cnt; + channel->lo = cnt - duty_cnt; + } + + channel->period_ns = period_ns; + channel->duty_ns = duty_ns; + channel->configured = true; + + if (channel->enabled) { + meson_pwm_set_enable(dev, channeln, false); + meson_pwm_set_enable(dev, channeln, true); + } + + return 0; +} + +static int meson_pwm_set_enable(struct udevice *dev, uint channeln, bool enable) +{ + struct meson_pwm *priv = dev_get_priv(dev); + struct meson_pwm_channel *channel; + struct meson_pwm_channel_data *channel_data; + u32 value; + + if (channeln >= MESON_NUM_PWMS) + return -ENODEV; + + channel = &priv->channels[channeln]; + channel_data = &meson_pwm_per_channel_data[channeln]; + + if (!channel->configured) + return -EINVAL; + + if (enable) { + if (channel->enabled) + return 0; + + value = readl(priv->base + REG_MISC_AB); + value &= ~(MISC_CLK_DIV_MASK << channel_data->clk_div_shift); + value |= channel->pre_div << channel_data->clk_div_shift; + value |= channel_data->clk_en_mask; + writel(value, priv->base + REG_MISC_AB); + + value = FIELD_PREP(PWM_HIGH_MASK, channel->hi) | + FIELD_PREP(PWM_LOW_MASK, channel->lo); + writel(value, priv->base + channel_data->reg_offset); + + value = readl(priv->base + REG_MISC_AB); + value |= channel_data->pwm_en_mask; + writel(value, priv->base + REG_MISC_AB); + + debug("%s%d: enabled\n", __func__, channeln); + channel->enabled = true; + } else { + if (!channel->enabled) + return 0; + + value = readl(priv->base + REG_MISC_AB); + value &= channel_data->pwm_en_mask; + writel(value, priv->base + REG_MISC_AB); + + debug("%s%d: disabled\n", __func__, channeln); + channel->enabled = false; + } + + return 0; +} + +static int meson_pwm_set_invert(struct udevice *dev, uint channeln, bool polarity) +{ + struct meson_pwm *priv = dev_get_priv(dev); + struct meson_pwm_channel *channel; + + if (channeln >= MESON_NUM_PWMS) + return -ENODEV; + + debug("%s%d: set invert %s\n", __func__, channeln, polarity ? "true" : "false"); + + channel = &priv->channels[channeln]; + + channel->polarity = polarity; + + if (!channel->configured) + return 0; + + return meson_pwm_set_config(dev, channeln, channel->period_ns, channel->duty_ns); +} + +static int meson_pwm_ofdata_to_platdata(struct udevice *dev) +{ + struct meson_pwm *priv = dev_get_priv(dev); + + priv->base = dev_read_addr_ptr(dev); + + return 0; +} + +static int meson_pwm_probe(struct udevice *dev) +{ + struct meson_pwm *priv = dev_get_priv(dev); + struct meson_pwm_data *data; + unsigned int i, p; + char name[255]; + int err; + u32 reg; + + data = (struct meson_pwm_data *)dev_get_driver_data(dev); + if (!data) + return -EINVAL; + + for (i = 0; i < MESON_NUM_PWMS; i++) { + struct meson_pwm_channel *channel = &priv->channels[i]; + struct meson_pwm_channel_data *channel_data = &meson_pwm_per_channel_data[i]; + + snprintf(name, sizeof(name), "clkin%u", i); + + err = clk_get_by_name(dev, name, &channel->clk); + /* If clock is not specified, use the already set clock */ + if (err == -ENODATA) { + struct udevice *cdev; + struct uclass *uc; + + /* Get parent from mux */ + p = (readl(priv->base + REG_MISC_AB) >> channel_data->clk_sel_shift) & + MISC_CLK_SEL_MASK; + + if (p >= data->num_parents) { + printf("%s%d: hw parent is invalid\n", __func__, i); + return -EINVAL; + } + + if (data->parent_ids[p] == -1) { + /* Search for xtal clk */ + const char *str; + + err = uclass_get(UCLASS_CLK, &uc); + if (err) + return err; + + uclass_foreach_dev(cdev, uc) { + if (strcmp(cdev->driver->name, "fixed_rate_clock")) + continue; + + str = ofnode_read_string(cdev->node, "clock-output-names"); + if (!str) + continue; + + if (!strcmp(str, "xtal")) { + err = uclass_get_device_by_ofnode(UCLASS_CLK, + cdev->node, + &cdev); + if (err) { + printf("%s%d: Failed to get xtal clk\n", __func__, i); + return err; + } + + break; + } + } + + if (!cdev) { + printf("%s%d: Failed to find xtal clk device\n", __func__, i); + return -EINVAL; + } + + channel->clk.dev = cdev; + channel->clk.id = 0; + channel->clk.data = 0; + } else { + /* Look for parent clock */ + err = uclass_get(UCLASS_CLK, &uc); + if (err) + return err; + + uclass_foreach_dev(cdev, uc) { + if (strstr(cdev->driver->name, "meson_clk")) + break; + } + + if (!cdev) { + printf("%s%d: Failed to find clk device\n", __func__, i); + return -EINVAL; + } + + err = uclass_get_device_by_ofnode(UCLASS_CLK, cdev->node, &cdev); + if (err) { + printf("%s%d: Failed to get clk controller\n", __func__, i); + return err; + } + + channel->clk.dev = cdev; + channel->clk.id = data->parent_ids[p]; + channel->clk.data = 0; + } + + /* We have our source clock, do not alter HW clock mux */ + continue; + } else + return err; + + /* Get id in list */ + for (p = 0 ; p < data->num_parents ; ++p) { + if (!strcmp(channel->clk.dev->driver->name, "fixed_rate_clock")) { + if (data->parent_ids[p] == -1) + break; + } else { + if (data->parent_ids[p] == channel->clk.id) + break; + } + } + + /* Invalid clock ID */ + if (p == data->num_parents) { + printf("%s%d: source clock is invalid\n", __func__, i); + return -EINVAL; + } + + /* switch parent in mux */ + reg = readl(priv->base + REG_MISC_AB); + + debug("%s%d: switching parent %d to %d\n", __func__, i, + (reg >> channel_data->clk_sel_shift) & MISC_CLK_SEL_MASK, p); + + reg &= MISC_CLK_SEL_MASK << channel_data->clk_sel_shift; + reg |= (p & MISC_CLK_SEL_MASK) << channel_data->clk_sel_shift; + writel(reg, priv->base + REG_MISC_AB); + } + + return 0; +} + +static const struct pwm_ops meson_pwm_ops = { + .set_config = meson_pwm_set_config, + .set_enable = meson_pwm_set_enable, + .set_invert = meson_pwm_set_invert, +}; + +#define XTAL -1 + +/* Local clock ids aliases to avoid define conflicts */ +#define GXBB_CLKID_HDMI_PLL 2 +#define GXBB_CLKID_FCLK_DIV3 5 +#define GXBB_CLKID_FCLK_DIV4 6 +#define GXBB_CLKID_CLK81 12 + +static const long pwm_gxbb_parent_ids[] = { + XTAL, GXBB_CLKID_HDMI_PLL, GXBB_CLKID_FCLK_DIV4, GXBB_CLKID_FCLK_DIV3 +}; + +static const struct meson_pwm_data pwm_gxbb_data = { + .parent_ids = pwm_gxbb_parent_ids, + .num_parents = ARRAY_SIZE(pwm_gxbb_parent_ids), +}; + +/* + * Only the 2 first inputs of the GXBB AO PWMs are valid + * The last 2 are grounded + */ +static const long pwm_gxbb_ao_parent_ids[] = { + XTAL, GXBB_CLKID_CLK81 +}; + +static const struct meson_pwm_data pwm_gxbb_ao_data = { + .parent_ids = pwm_gxbb_ao_parent_ids, + .num_parents = ARRAY_SIZE(pwm_gxbb_ao_parent_ids), +}; + +/* Local clock ids aliases to avoid define conflicts */ +#define AXG_CLKID_FCLK_DIV3 3 +#define AXG_CLKID_FCLK_DIV4 4 +#define AXG_CLKID_FCLK_DIV5 5 +#define AXG_CLKID_CLK81 10 + +static const long pwm_axg_ee_parent_ids[] = { + XTAL, AXG_CLKID_FCLK_DIV5, AXG_CLKID_FCLK_DIV4, AXG_CLKID_FCLK_DIV3 +}; + +static const struct meson_pwm_data pwm_axg_ee_data = { + .parent_ids = pwm_axg_ee_parent_ids, + .num_parents = ARRAY_SIZE(pwm_axg_ee_parent_ids), +}; + +static const long pwm_axg_ao_parent_ids[] = { + AXG_CLKID_CLK81, XTAL, AXG_CLKID_FCLK_DIV4, AXG_CLKID_FCLK_DIV5 +}; + +static const struct meson_pwm_data pwm_axg_ao_data = { + .parent_ids = pwm_axg_ao_parent_ids, + .num_parents = ARRAY_SIZE(pwm_axg_ao_parent_ids), +}; + +/* Local clock ids aliases to avoid define conflicts */ +#define G12A_CLKID_FCLK_DIV3 3 +#define G12A_CLKID_FCLK_DIV4 4 +#define G12A_CLKID_FCLK_DIV5 5 +#define G12A_CLKID_CLK81 10 +#define G12A_CLKID_HDMI_PLL 128 + +static const long pwm_g12a_ao_ab_parent_ids[] = { + XTAL, G12A_CLKID_CLK81, G12A_CLKID_FCLK_DIV4, G12A_CLKID_FCLK_DIV5 +}; + +static const struct meson_pwm_data pwm_g12a_ao_ab_data = { + .parent_ids = pwm_g12a_ao_ab_parent_ids, + .num_parents = ARRAY_SIZE(pwm_g12a_ao_ab_parent_ids), +}; + +static const long pwm_g12a_ao_cd_parent_ids[] = { + XTAL, G12A_CLKID_CLK81, +}; + +static const struct meson_pwm_data pwm_g12a_ao_cd_data = { + .parent_ids = pwm_g12a_ao_cd_parent_ids, + .num_parents = ARRAY_SIZE(pwm_g12a_ao_cd_parent_ids), +}; + +static const long pwm_g12a_ee_parent_ids[] = { + XTAL, G12A_CLKID_HDMI_PLL, G12A_CLKID_FCLK_DIV4, G12A_CLKID_FCLK_DIV3 +}; + +static const struct meson_pwm_data pwm_g12a_ee_data = { + .parent_ids = pwm_g12a_ee_parent_ids, + .num_parents = ARRAY_SIZE(pwm_g12a_ee_parent_ids), +}; + +static const struct udevice_id meson_pwm_ids[] = { + { + .compatible = "amlogic,meson-gxbb-pwm", + .data = (ulong)&pwm_gxbb_data + }, + { + .compatible = "amlogic,meson-gxbb-ao-pwm", + .data = (ulong)&pwm_gxbb_ao_data + }, + { + .compatible = "amlogic,meson-axg-ee-pwm", + .data = (ulong)&pwm_axg_ee_data + }, + { + .compatible = "amlogic,meson-axg-ao-pwm", + .data = (ulong)&pwm_axg_ao_data + }, + { + .compatible = "amlogic,meson-g12a-ee-pwm", + .data = (ulong)&pwm_g12a_ee_data + }, + { + .compatible = "amlogic,meson-g12a-ao-pwm-ab", + .data = (ulong)&pwm_g12a_ao_ab_data + }, + { + .compatible = "amlogic,meson-g12a-ao-pwm-cd", + .data = (ulong)&pwm_g12a_ao_cd_data + }, +}; + +U_BOOT_DRIVER(meson_pwm) = { + .name = "meson_pwm", + .id = UCLASS_PWM, + .of_match = meson_pwm_ids, + .ops = &meson_pwm_ops, + .ofdata_to_platdata = meson_pwm_ofdata_to_platdata, + .probe = meson_pwm_probe, + .priv_auto_alloc_size = sizeof(struct meson_pwm), +}; -- cgit v1.3.1