diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/Kconfig | 2 | ||||
-rw-r--r-- | drivers/Makefile | 1 | ||||
-rw-r--r-- | drivers/block/Kconfig | 10 | ||||
-rw-r--r-- | drivers/block/Makefile | 1 | ||||
-rw-r--r-- | drivers/block/dwc_ahci.c | 101 | ||||
-rw-r--r-- | drivers/clk/at91/clk-master.c | 1 | ||||
-rw-r--r-- | drivers/clk/at91/clk-peripheral.c | 29 | ||||
-rw-r--r-- | drivers/clk/at91/pmc.c | 6 | ||||
-rw-r--r-- | drivers/net/macb.c | 52 | ||||
-rw-r--r-- | drivers/phy/Kconfig | 62 | ||||
-rw-r--r-- | drivers/phy/Makefile | 10 | ||||
-rw-r--r-- | drivers/phy/phy-uclass.c | 139 | ||||
-rw-r--r-- | drivers/phy/sandbox-phy.c | 108 | ||||
-rw-r--r-- | drivers/phy/ti-pipe3-phy.c | 373 | ||||
-rw-r--r-- | drivers/serial/atmel_usart.c | 84 |
15 files changed, 953 insertions, 26 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index a096dad2b2..a5f24d72da 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -54,6 +54,8 @@ source "drivers/pci/Kconfig" source "drivers/pcmcia/Kconfig" +source "drivers/phy/Kconfig" + source "drivers/phy/marvell/Kconfig" source "drivers/pinctrl/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 4a4b2377c5..691642023d 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_$(SPL_TPL_)DM) += core/ obj-$(CONFIG_$(SPL_)CLK) += clk/ obj-$(CONFIG_$(SPL_)LED) += led/ +obj-$(CONFIG_$(SPL_)PHY) += phy/ obj-$(CONFIG_$(SPL_)PINCTRL) += pinctrl/ obj-$(CONFIG_$(SPL_)RAM) += ram/ diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 88e66e2377..6cbe1454b8 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -48,4 +48,14 @@ config SATA_CEVA ZynqMP. Support up to 2 external devices. Complient with SATA 3.1 and AHCI 1.3 specifications with hot-plug detect feature. + +config DWC_AHCI + bool "Enable Synopsys DWC AHCI driver support" + select SCSI_AHCI + select PHY + depends on DM_SCSI + help + Enable this driver to support Sata devices through + Synopsys DWC AHCI module. + endmenu diff --git a/drivers/block/Makefile b/drivers/block/Makefile index f415b3371b..d89c8b0574 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -11,6 +11,7 @@ ifndef CONFIG_BLK obj-y += blk_legacy.o endif +obj-$(CONFIG_DWC_AHCI) += dwc_ahci.o obj-$(CONFIG_AHCI) += ahci-uclass.o obj-$(CONFIG_DM_SCSI) += scsi-uclass.o obj-$(CONFIG_SCSI_AHCI) += ahci.o diff --git a/drivers/block/dwc_ahci.c b/drivers/block/dwc_ahci.c new file mode 100644 index 0000000000..d5bb0b887a --- /dev/null +++ b/drivers/block/dwc_ahci.c @@ -0,0 +1,101 @@ +/* + * DWC SATA platform driver + * + * (C) Copyright 2016 + * Texas Instruments Incorporated, <www.ti.com> + * + * Author: Mugunthan V N <mugunthanvnm@ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <ahci.h> +#include <scsi.h> +#include <sata.h> +#include <asm/arch/sata.h> +#include <asm/io.h> +#include <generic-phy.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct dwc_ahci_priv { + void *base; + void *wrapper_base; +}; + +static int dwc_ahci_ofdata_to_platdata(struct udevice *dev) +{ + struct dwc_ahci_priv *priv = dev_get_priv(dev); + struct scsi_platdata *plat = dev_get_platdata(dev); + fdt_addr_t addr; + + plat->max_id = fdtdec_get_uint(gd->fdt_blob, dev->of_offset, "max-id", + CONFIG_SYS_SCSI_MAX_SCSI_ID); + plat->max_lun = fdtdec_get_uint(gd->fdt_blob, dev->of_offset, + "max-lun", CONFIG_SYS_SCSI_MAX_LUN); + + priv->base = map_physmem(dev_get_addr(dev), sizeof(void *), + MAP_NOCACHE); + + addr = dev_get_addr_index(dev, 1); + if (addr != FDT_ADDR_T_NONE) { + priv->wrapper_base = map_physmem(addr, sizeof(void *), + MAP_NOCACHE); + } else { + priv->wrapper_base = NULL; + } + + return 0; +} + +static int dwc_ahci_probe(struct udevice *dev) +{ + struct dwc_ahci_priv *priv = dev_get_priv(dev); + int ret; + struct phy phy; + + ret = generic_phy_get_by_name(dev, "sata-phy", &phy); + if (ret) { + error("can't get the phy from DT\n"); + return ret; + } + + ret = generic_phy_init(&phy); + if (ret) { + error("unable to initialize the sata phy\n"); + return ret; + } + + ret = generic_phy_power_on(&phy); + if (ret) { + error("unable to power on the sata phy\n"); + return ret; + } + + if (priv->wrapper_base) { + u32 val = TI_SATA_IDLE_NO | TI_SATA_STANDBY_NO; + + /* Enable SATA module, No Idle, No Standby */ + writel(val, priv->wrapper_base + TI_SATA_SYSCONFIG); + } + + return ahci_init(priv->base); +} + +static const struct udevice_id dwc_ahci_ids[] = { + { .compatible = "snps,dwc-ahci" }, + { } +}; + +U_BOOT_DRIVER(dwc_ahci) = { + .name = "dwc_ahci", + .id = UCLASS_SCSI, + .of_match = dwc_ahci_ids, + .ofdata_to_platdata = dwc_ahci_ofdata_to_platdata, + .probe = dwc_ahci_probe, + .priv_auto_alloc_size = sizeof(struct dwc_ahci_priv), + .platdata_auto_alloc_size = sizeof(struct scsi_platdata), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c index 284b248271..72d0a739f1 100644 --- a/drivers/clk/at91/clk-master.c +++ b/drivers/clk/at91/clk-master.c @@ -21,6 +21,7 @@ static struct clk_ops at91_master_clk_ops = { }; static const struct udevice_id at91_master_clk_match[] = { + { .compatible = "atmel,at91rm9200-clk-master" }, { .compatible = "atmel,at91sam9x5-clk-master" }, {} }; diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c index e1ed447133..62fabe304d 100644 --- a/drivers/clk/at91/clk-peripheral.c +++ b/drivers/clk/at91/clk-peripheral.c @@ -16,6 +16,10 @@ #define PERIPHERAL_ID_MAX 31 #define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX)) +enum periph_clk_type { + CLK_PERIPH_AT91RM9200 = 0, + CLK_PERIPH_AT91SAM9X5, +}; /** * sam9x5_periph_clk_bind() - for the periph clock driver * Recursively bind its children as clk devices. @@ -28,7 +32,14 @@ static int sam9x5_periph_clk_bind(struct udevice *dev) } static const struct udevice_id sam9x5_periph_clk_match[] = { - { .compatible = "atmel,at91sam9x5-clk-peripheral" }, + { + .compatible = "atmel,at91rm9200-clk-peripheral", + .data = CLK_PERIPH_AT91RM9200, + }, + { + .compatible = "atmel,at91sam9x5-clk-peripheral", + .data = CLK_PERIPH_AT91SAM9X5, + }, {} }; @@ -45,12 +56,24 @@ static int periph_clk_enable(struct clk *clk) { struct pmc_platdata *plat = dev_get_platdata(clk->dev); struct at91_pmc *pmc = plat->reg_base; + enum periph_clk_type clk_type; + void *addr; if (clk->id < PERIPHERAL_ID_MIN) return -1; - writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr); - setbits_le32(&pmc->pcr, AT91_PMC_PCR_CMD_WRITE | AT91_PMC_PCR_EN); + clk_type = dev_get_driver_data(dev_get_parent(clk->dev)); + if (clk_type == CLK_PERIPH_AT91RM9200) { + addr = &pmc->pcer; + if (clk->id > PERIPHERAL_ID_MAX) + addr = &pmc->pcer1; + + setbits_le32(addr, PERIPHERAL_MASK(clk->id)); + } else { + writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr); + setbits_le32(&pmc->pcr, + AT91_PMC_PCR_CMD_WRITE | AT91_PMC_PCR_EN); + } return 0; } diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index fcd693a2f6..72d52c5818 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -16,6 +16,12 @@ DECLARE_GLOBAL_DATA_PTR; static const struct udevice_id at91_pmc_match[] = { + { .compatible = "atmel,at91rm9200-pmc" }, + { .compatible = "atmel,at91sam9260-pmc" }, + { .compatible = "atmel,at91sam9g45-pmc" }, + { .compatible = "atmel,at91sam9n12-pmc" }, + { .compatible = "atmel,at91sam9x5-pmc" }, + { .compatible = "atmel,sama5d3-pmc" }, { .compatible = "atmel,sama5d2-pmc" }, {} }; diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 402e866817..21a620c244 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -111,6 +111,9 @@ struct macb_device { #endif unsigned short phy_addr; struct mii_dev *bus; +#ifdef CONFIG_PHYLIB + struct phy_device *phydev; +#endif #ifdef CONFIG_DM_ETH #ifdef CONFIG_CLK @@ -479,9 +482,6 @@ static int macb_phy_init(struct macb_device *macb, const char *name) #ifdef CONFIG_DM_ETH struct macb_device *macb = dev_get_priv(dev); #endif -#ifdef CONFIG_PHYLIB - struct phy_device *phydev; -#endif u32 ncfgr; u16 phy_id, status, adv, lpa; int media, speed, duplex; @@ -503,19 +503,19 @@ static int macb_phy_init(struct macb_device *macb, const char *name) #ifdef CONFIG_PHYLIB #ifdef CONFIG_DM_ETH - phydev = phy_connect(macb->bus, macb->phy_addr, dev, + macb->phydev = phy_connect(macb->bus, macb->phy_addr, dev, macb->phy_interface); #else /* need to consider other phy interface mode */ - phydev = phy_connect(macb->bus, macb->phy_addr, &macb->netdev, + macb->phydev = phy_connect(macb->bus, macb->phy_addr, &macb->netdev, PHY_INTERFACE_MODE_RGMII); #endif - if (!phydev) { + if (!macb->phydev) { printf("phy_connect failed\n"); return -ENODEV; } - phy_config(phydev); + phy_config(macb->phydev); #endif status = macb_mdio_read(macb, MII_BMSR); @@ -1035,6 +1035,7 @@ static int macb_eth_probe(struct udevice *dev) struct eth_pdata *pdata = dev_get_platdata(dev); struct macb_device *macb = dev_get_priv(dev); const char *phy_mode; + __maybe_unused int ret; phy_mode = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "phy-mode", NULL); @@ -1048,7 +1049,7 @@ static int macb_eth_probe(struct udevice *dev) macb->regs = (void *)pdata->iobase; #ifdef CONFIG_CLK - int ret = macb_enable_clk(dev); + ret = macb_enable_clk(dev); if (ret) return ret; #endif @@ -1056,23 +1057,35 @@ static int macb_eth_probe(struct udevice *dev) _macb_eth_initialize(macb); #if defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB) - int retval; - struct mii_dev *mdiodev = mdio_alloc(); - if (!mdiodev) + macb->bus = mdio_alloc(); + if (!macb->bus) return -ENOMEM; - strncpy(mdiodev->name, dev->name, MDIO_NAME_LEN); - mdiodev->read = macb_miiphy_read; - mdiodev->write = macb_miiphy_write; + strncpy(macb->bus->name, dev->name, MDIO_NAME_LEN); + macb->bus->read = macb_miiphy_read; + macb->bus->write = macb_miiphy_write; - retval = mdio_register(mdiodev); - if (retval < 0) - return retval; + ret = mdio_register(macb->bus); + if (ret < 0) + return ret; macb->bus = miiphy_get_dev_by_name(dev->name); #endif return 0; } +static int macb_eth_remove(struct udevice *dev) +{ + struct macb_device *macb = dev_get_priv(dev); + +#ifdef CONFIG_PHYLIB + free(macb->phydev); +#endif + mdio_unregister(macb->bus); + mdio_free(macb->bus); + + return 0; +} + static int macb_eth_ofdata_to_platdata(struct udevice *dev) { struct eth_pdata *pdata = dev_get_platdata(dev); @@ -1083,6 +1096,10 @@ static int macb_eth_ofdata_to_platdata(struct udevice *dev) static const struct udevice_id macb_eth_ids[] = { { .compatible = "cdns,macb" }, + { .compatible = "cdns,at91sam9260-macb" }, + { .compatible = "atmel,sama5d2-gem" }, + { .compatible = "atmel,sama5d3-gem" }, + { .compatible = "atmel,sama5d4-gem" }, { } }; @@ -1092,6 +1109,7 @@ U_BOOT_DRIVER(eth_macb) = { .of_match = macb_eth_ids, .ofdata_to_platdata = macb_eth_ofdata_to_platdata, .probe = macb_eth_probe, + .remove = macb_eth_remove, .ops = &macb_eth_ops, .priv_auto_alloc_size = sizeof(struct macb_device), .platdata_auto_alloc_size = sizeof(struct eth_pdata), diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig new file mode 100644 index 0000000000..a91a6946b8 --- /dev/null +++ b/drivers/phy/Kconfig @@ -0,0 +1,62 @@ + +menu "PHY Subsystem" + +config PHY + bool "PHY Core" + depends on DM + help + PHY support. + + This framework is designed to provide a generic interface for PHY + devices. PHY devices are dedicated hardware that handle the physical + layer of the protocols in the OSI model. + PHYs are commonly used for high speed interfaces such as Serial-ATA + or PCI express. + The API provides functions to initialize/deinitialize the + PHY, power on/off the PHY, and reset the PHY. It's meant to be as + compatible as possible with the equivalent framework found in the + linux kernel. + +config SPL_PHY + bool "PHY Core in SPL" + depends on DM + help + PHY support in SPL. + + This framework is designed to provide a generic interface for PHY + devices. PHY devices are dedicated hardware that handle the physical + layer of the protocols (https://en.wikipedia.org/wiki/OSI_model). + PHYs are commonly used for high speed interfaces such as Serial-ATA + or PCI express. + The API provides functions to initialize/deinitialize the + PHY, power on/off the PHY, and reset the PHY. It's meant to be as + compatible as possible with the equivalent framework found in the + linux kernel. + +config PHY_SANDBOX + bool "Sandbox PHY support" + depends on SANDBOX + depends on PHY + help + This select a dummy sandbox PHY driver. It used only to implement + the unit tests for the phy framework + +config PIPE3_PHY + bool "Support omap's PIPE3 PHY" + depends on PHY && ARCH_OMAP2 + help + Support for the omap PIPE3 phy for sata + + This PHY is found on omap devices supporting SATA such as dra7, am57x + and omap5 + +config SPL_PIPE3_PHY + bool "Support omap's PIPE3 PHY in SPL" + depends on SPL_PHY && ARCH_OMAP2 + help + Support for the omap PIPE3 phy for sata in SPL + + This PHY is found on omap devices supporting SATA such as dra7, am57x + and omap5 + +endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile new file mode 100644 index 0000000000..6ce96d2ccc --- /dev/null +++ b/drivers/phy/Makefile @@ -0,0 +1,10 @@ +# +# Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ +# Written by Jean-Jacques Hiblot <jjhiblot@ti.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_$(SPL_)PHY) += phy-uclass.o +obj-$(CONFIG_PHY_SANDBOX) += sandbox-phy.o +obj-$(CONFIG_$(SPL_)PIPE3_PHY) += ti-pipe3-phy.o diff --git a/drivers/phy/phy-uclass.c b/drivers/phy/phy-uclass.c new file mode 100644 index 0000000000..0d8bef76db --- /dev/null +++ b/drivers/phy/phy-uclass.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Written by Jean-Jacques Hiblot <jjhiblot@ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <generic-phy.h> + +DECLARE_GLOBAL_DATA_PTR; + +static inline struct phy_ops *phy_dev_ops(struct udevice *dev) +{ + return (struct phy_ops *)dev->driver->ops; +} + +static int generic_phy_xlate_offs_flags(struct phy *phy, + struct fdtdec_phandle_args *args) +{ + debug("%s(phy=%p)\n", __func__, phy); + + if (args->args_count > 1) { + debug("Invaild args_count: %d\n", args->args_count); + return -EINVAL; + } + + if (args->args_count) + phy->id = args->args[0]; + else + phy->id = 0; + + + return 0; +} + +int generic_phy_get_by_index(struct udevice *dev, int index, + struct phy *phy) +{ + struct fdtdec_phandle_args args; + struct phy_ops *ops; + int ret; + struct udevice *phydev; + + debug("%s(dev=%p, index=%d, phy=%p)\n", __func__, dev, index, phy); + + assert(phy); + ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev_of_offset(dev), + "phys", "#phy-cells", 0, index, + &args); + if (ret) { + debug("%s: fdtdec_parse_phandle_with_args failed: err=%d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_of_offset(UCLASS_PHY, args.node, &phydev); + if (ret) { + debug("%s: uclass_get_device_by_of_offset failed: err=%d\n", + __func__, ret); + return ret; + } + + phy->dev = phydev; + + ops = phy_dev_ops(phydev); + + if (ops->of_xlate) + ret = ops->of_xlate(phy, &args); + else + ret = generic_phy_xlate_offs_flags(phy, &args); + if (ret) { + debug("of_xlate() failed: %d\n", ret); + goto err; + } + + return 0; + +err: + return ret; +} + +int generic_phy_get_by_name(struct udevice *dev, const char *phy_name, + struct phy *phy) +{ + int index; + + debug("%s(dev=%p, name=%s, phy=%p)\n", __func__, dev, phy_name, phy); + + index = fdt_stringlist_search(gd->fdt_blob, dev_of_offset(dev), + "phy-names", phy_name); + if (index < 0) { + debug("fdt_stringlist_search() failed: %d\n", index); + return index; + } + + return generic_phy_get_by_index(dev, index, phy); +} + +int generic_phy_init(struct phy *phy) +{ + struct phy_ops const *ops = phy_dev_ops(phy->dev); + + return ops->init ? ops->init(phy) : 0; +} + +int generic_phy_reset(struct phy *phy) +{ + struct phy_ops const *ops = phy_dev_ops(phy->dev); + + return ops->reset ? ops->reset(phy) : 0; +} + +int generic_phy_exit(struct phy *phy) +{ + struct phy_ops const *ops = phy_dev_ops(phy->dev); + + return ops->exit ? ops->exit(phy) : 0; +} + +int generic_phy_power_on(struct phy *phy) +{ + struct phy_ops const *ops = phy_dev_ops(phy->dev); + + return ops->power_on ? ops->power_on(phy) : 0; +} + +int generic_phy_power_off(struct phy *phy) +{ + struct phy_ops const *ops = phy_dev_ops(phy->dev); + + return ops->power_off ? ops->power_off(phy) : 0; +} + +UCLASS_DRIVER(phy) = { + .id = UCLASS_PHY, + .name = "phy", +}; diff --git a/drivers/phy/sandbox-phy.c b/drivers/phy/sandbox-phy.c new file mode 100644 index 0000000000..9ad820c24c --- /dev/null +++ b/drivers/phy/sandbox-phy.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Written by Jean-Jacques Hiblot <jjhiblot@ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <generic-phy.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct sandbox_phy_priv { + bool initialized; + bool on; + bool broken; +}; + +static int sandbox_phy_power_on(struct phy *phy) +{ + struct sandbox_phy_priv *priv = dev_get_priv(phy->dev); + + if (!priv->initialized) + return -EIO; + + if (priv->broken) + return -EIO; + + priv->on = true; + + return 0; +} + +static int sandbox_phy_power_off(struct phy *phy) +{ + struct sandbox_phy_priv *priv = dev_get_priv(phy->dev); + + if (!priv->initialized) + return -EIO; + + if (priv->broken) + return -EIO; + + /* + * for validation purpose, let's says that power off + * works only for PHY 0 + */ + if (phy->id) + return -EIO; + + priv->on = false; + + return 0; +} + +static int sandbox_phy_init(struct phy *phy) +{ + struct sandbox_phy_priv *priv = dev_get_priv(phy->dev); + + priv->initialized = true; + priv->on = true; + + return 0; +} + +static int sandbox_phy_exit(struct phy *phy) +{ + struct sandbox_phy_priv *priv = dev_get_priv(phy->dev); + + priv->initialized = false; + priv->on = false; + + return 0; +} + +static int sandbox_phy_probe(struct udevice *dev) +{ + struct sandbox_phy_priv *priv = dev_get_priv(dev); + + priv->initialized = false; + priv->on = false; + priv->broken = fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), + "broken"); + + return 0; +} + +static struct phy_ops sandbox_phy_ops = { + .power_on = sandbox_phy_power_on, + .power_off = sandbox_phy_power_off, + .init = sandbox_phy_init, + .exit = sandbox_phy_exit, +}; + +static const struct udevice_id sandbox_phy_ids[] = { + { .compatible = "sandbox,phy" }, + { } +}; + +U_BOOT_DRIVER(phy_sandbox) = { + .name = "phy_sandbox", + .id = UCLASS_PHY, + .of_match = sandbox_phy_ids, + .ops = &sandbox_phy_ops, + .probe = sandbox_phy_probe, + .priv_auto_alloc_size = sizeof(struct sandbox_phy_priv), +}; diff --git a/drivers/phy/ti-pipe3-phy.c b/drivers/phy/ti-pipe3-phy.c new file mode 100644 index 0000000000..ed80f0ff0b --- /dev/null +++ b/drivers/phy/ti-pipe3-phy.c @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Written by Jean-Jacques Hiblot <jjhiblot@ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <dm/device.h> +#include <generic-phy.h> +#include <asm/io.h> +#include <asm/arch/sys_proto.h> +#include <syscon.h> +#include <regmap.h> + +/* PLLCTRL Registers */ +#define PLL_STATUS 0x00000004 +#define PLL_GO 0x00000008 +#define PLL_CONFIGURATION1 0x0000000C +#define PLL_CONFIGURATION2 0x00000010 +#define PLL_CONFIGURATION3 0x00000014 +#define PLL_CONFIGURATION4 0x00000020 + +#define PLL_REGM_MASK 0x001FFE00 +#define PLL_REGM_SHIFT 9 +#define PLL_REGM_F_MASK 0x0003FFFF +#define PLL_REGM_F_SHIFT 0 +#define PLL_REGN_MASK 0x000001FE +#define PLL_REGN_SHIFT 1 +#define PLL_SELFREQDCO_MASK 0x0000000E +#define PLL_SELFREQDCO_SHIFT 1 +#define PLL_SD_MASK 0x0003FC00 +#define PLL_SD_SHIFT 10 +#define SET_PLL_GO 0x1 +#define PLL_TICOPWDN BIT(16) +#define PLL_LDOPWDN BIT(15) +#define PLL_LOCK 0x2 +#define PLL_IDLE 0x1 + +/* Software rest for the SATA PLL (in CTRL_CORE_SMA_SW_0 register)*/ +#define SATA_PLL_SOFT_RESET (1<<18) + +/* PHY POWER CONTROL Register */ +#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK 0x003FC000 +#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT 0xE + +#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK 0xFFC00000 +#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT 0x16 + +#define OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON 0x3 +#define OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF 0x0 + + +#define PLL_IDLE_TIME 100 /* in milliseconds */ +#define PLL_LOCK_TIME 100 /* in milliseconds */ + +struct omap_pipe3 { + void __iomem *pll_ctrl_base; + void __iomem *power_reg; + void __iomem *pll_reset_reg; + struct pipe3_dpll_map *dpll_map; +}; + + +struct pipe3_dpll_params { + u16 m; + u8 n; + u8 freq:3; + u8 sd; + u32 mf; +}; + +struct pipe3_dpll_map { + unsigned long rate; + struct pipe3_dpll_params params; +}; + +static inline u32 omap_pipe3_readl(void __iomem *addr, unsigned offset) +{ + return readl(addr + offset); +} + +static inline void omap_pipe3_writel(void __iomem *addr, unsigned offset, + u32 data) +{ + writel(data, addr + offset); +} + +static struct pipe3_dpll_params *omap_pipe3_get_dpll_params(struct omap_pipe3 + *pipe3) +{ + u32 rate; + struct pipe3_dpll_map *dpll_map = pipe3->dpll_map; + + rate = get_sys_clk_freq(); + + for (; dpll_map->rate; dpll_map++) { + if (rate == dpll_map->rate) + return &dpll_map->params; + } + + printf("%s: No DPLL configuration for %u Hz SYS CLK\n", + __func__, rate); + return NULL; +} + +static int omap_pipe3_wait_lock(struct omap_pipe3 *pipe3) +{ + u32 val; + int timeout = PLL_LOCK_TIME; + + do { + mdelay(1); + val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_STATUS); + if (val & PLL_LOCK) + break; + } while (--timeout); + + if (!(val & PLL_LOCK)) { + printf("%s: DPLL failed to lock\n", __func__); + return -EBUSY; + } + + return 0; +} + +static int omap_pipe3_dpll_program(struct omap_pipe3 *pipe3) +{ + u32 val; + struct pipe3_dpll_params *dpll_params; + + dpll_params = omap_pipe3_get_dpll_params(pipe3); + if (!dpll_params) { + printf("%s: Invalid DPLL parameters\n", __func__); + return -EINVAL; + } + + val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION1); + val &= ~PLL_REGN_MASK; + val |= dpll_params->n << PLL_REGN_SHIFT; + omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION1, val); + + val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION2); + val &= ~PLL_SELFREQDCO_MASK; + val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT; + omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION2, val); + + val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION1); + val &= ~PLL_REGM_MASK; + val |= dpll_params->m << PLL_REGM_SHIFT; + omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION1, val); + + val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION4); + val &= ~PLL_REGM_F_MASK; + val |= dpll_params->mf << PLL_REGM_F_SHIFT; + omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION4, val); + + val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION3); + val &= ~PLL_SD_MASK; + val |= dpll_params->sd << PLL_SD_SHIFT; + omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION3, val); + + omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_GO, SET_PLL_GO); + + return omap_pipe3_wait_lock(pipe3); +} + +static void omap_control_pipe3_power(struct omap_pipe3 *pipe3, int on) +{ + u32 val, rate; + + val = readl(pipe3->power_reg); + + rate = get_sys_clk_freq(); + rate = rate/1000000; + + if (on) { + val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK | + OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK); + val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON << + OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; + val |= rate << + OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; + } else { + val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK; + val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF << + OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; + } + + writel(val, pipe3->power_reg); +} + +static int pipe3_init(struct phy *phy) +{ + int ret; + u32 val; + struct omap_pipe3 *pipe3 = dev_get_priv(phy->dev); + + /* Program the DPLL only if not locked */ + val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_STATUS); + if (!(val & PLL_LOCK)) { + ret = omap_pipe3_dpll_program(pipe3); + if (ret) + return ret; + } else { + /* else just bring it out of IDLE mode */ + val = omap_pipe3_readl(pipe3->pll_ctrl_base, + PLL_CONFIGURATION2); + if (val & PLL_IDLE) { + val &= ~PLL_IDLE; + omap_pipe3_writel(pipe3->pll_ctrl_base, + PLL_CONFIGURATION2, val); + ret = omap_pipe3_wait_lock(pipe3); + if (ret) + return ret; + } + } + return 0; +} + +static int pipe3_power_on(struct phy *phy) +{ + struct omap_pipe3 *pipe3 = dev_get_priv(phy->dev); + + /* Power up the PHY */ + omap_control_pipe3_power(pipe3, 1); + + return 0; +} + +static int pipe3_power_off(struct phy *phy) +{ + struct omap_pipe3 *pipe3 = dev_get_priv(phy->dev); + + /* Power down the PHY */ + omap_control_pipe3_power(pipe3, 0); + + return 0; +} + +static int pipe3_exit(struct phy *phy) +{ + u32 val; + int timeout = PLL_IDLE_TIME; + struct omap_pipe3 *pipe3 = dev_get_priv(phy->dev); + + pipe3_power_off(phy); + + /* Put DPLL in IDLE mode */ + val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION2); + val |= PLL_IDLE; + omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION2, val); + + /* wait for LDO and Oscillator to power down */ + do { + mdelay(1); + val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_STATUS); + if ((val & PLL_TICOPWDN) && (val & PLL_LDOPWDN)) + break; + } while (--timeout); + + if (!(val & PLL_TICOPWDN) || !(val & PLL_LDOPWDN)) { + error("%s: Failed to power down DPLL: PLL_STATUS 0x%x\n", + __func__, val); + return -EBUSY; + } + + val = readl(pipe3->pll_reset_reg); + writel(val | SATA_PLL_SOFT_RESET, pipe3->pll_reset_reg); + mdelay(1); + writel(val & ~SATA_PLL_SOFT_RESET, pipe3->pll_reset_reg); + return 0; +} + +static void *get_reg(struct udevice *dev, const char *name) +{ + struct udevice *syscon; + struct regmap *regmap; + const fdt32_t *cell; + int len, err; + void *base; + + err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, + name, &syscon); + if (err) { + error("unable to find syscon device for %s (%d)\n", + name, err); + return NULL; + } + + regmap = syscon_get_regmap(syscon); + if (IS_ERR(regmap)) { + error("unable to find regmap for %s (%ld)\n", + name, PTR_ERR(regmap)); + return NULL; + } + + cell = fdt_getprop(gd->fdt_blob, dev->of_offset, name, + &len); + if (len < 2*sizeof(fdt32_t)) { + error("offset not available for %s\n", name); + return NULL; + } + + base = regmap_get_range(regmap, 0); + if (!base) + return NULL; + + return fdtdec_get_number(cell + 1, 1) + base; +} + +static int pipe3_phy_probe(struct udevice *dev) +{ + fdt_addr_t addr; + fdt_size_t sz; + struct omap_pipe3 *pipe3 = dev_get_priv(dev); + + addr = dev_get_addr_size_index(dev, 2, &sz); + if (addr == FDT_ADDR_T_NONE) { + error("missing pll ctrl address\n"); + return -EINVAL; + } + + pipe3->pll_ctrl_base = map_physmem(addr, sz, MAP_NOCACHE); + if (!pipe3->pll_ctrl_base) { + error("unable to remap pll ctrl\n"); + return -EINVAL; + } + + pipe3->power_reg = get_reg(dev, "syscon-phy-power"); + if (!pipe3->power_reg) + return -EINVAL; + + pipe3->pll_reset_reg = get_reg(dev, "syscon-pllreset"); + if (!pipe3->pll_reset_reg) + return -EINVAL; + + pipe3->dpll_map = (struct pipe3_dpll_map *)dev_get_driver_data(dev); + + return 0; +} + +static struct pipe3_dpll_map dpll_map_sata[] = { + {12000000, {1000, 7, 4, 6, 0} }, /* 12 MHz */ + {16800000, {714, 7, 4, 6, 0} }, /* 16.8 MHz */ + {19200000, {625, 7, 4, 6, 0} }, /* 19.2 MHz */ + {20000000, {600, 7, 4, 6, 0} }, /* 20 MHz */ + {26000000, {461, 7, 4, 6, 0} }, /* 26 MHz */ + {38400000, {312, 7, 4, 6, 0} }, /* 38.4 MHz */ + { }, /* Terminator */ +}; + +static const struct udevice_id pipe3_phy_ids[] = { + { .compatible = "ti,phy-pipe3-sata", .data = (ulong)&dpll_map_sata }, + { } +}; + +static struct phy_ops pipe3_phy_ops = { + .init = pipe3_init, + .power_on = pipe3_power_on, + .power_off = pipe3_power_off, + .exit = pipe3_exit, +}; + +U_BOOT_DRIVER(pipe3_phy) = { + .name = "pipe3_phy", + .id = UCLASS_PHY, + .of_match = pipe3_phy_ids, + .ops = &pipe3_phy_ops, + .probe = pipe3_phy_probe, + .priv_auto_alloc_size = sizeof(struct omap_pipe3), +}; diff --git a/drivers/serial/atmel_usart.c b/drivers/serial/atmel_usart.c index 7674f97e8d..453f8eb451 100644 --- a/drivers/serial/atmel_usart.c +++ b/drivers/serial/atmel_usart.c @@ -7,6 +7,7 @@ * SPDX-License-Identifier: GPL-2.0+ */ #include <common.h> +#include <clk.h> #include <dm.h> #include <errno.h> #include <watchdog.h> @@ -25,6 +26,7 @@ DECLARE_GLOBAL_DATA_PTR; +#ifndef CONFIG_DM_SERIAL static void atmel_serial_setbrg_internal(atmel_usart3_t *usart, int id, int baudrate) { @@ -66,7 +68,6 @@ static void atmel_serial_activate(atmel_usart3_t *usart) __udelay(100); } -#ifndef CONFIG_DM_SERIAL static void atmel_serial_setbrg(void) { atmel_serial_setbrg_internal((atmel_usart3_t *)CONFIG_USART_BASE, @@ -133,17 +134,47 @@ __weak struct serial_device *default_serial_console(void) #endif #ifdef CONFIG_DM_SERIAL +enum serial_clk_type { + CLK_TYPE_NORMAL = 0, + CLK_TYPE_DBGU, +}; struct atmel_serial_priv { atmel_usart3_t *usart; + ulong usart_clk_rate; }; +static void _atmel_serial_set_brg(atmel_usart3_t *usart, + ulong usart_clk_rate, int baudrate) +{ + unsigned long divisor; + + divisor = (usart_clk_rate / 16 + baudrate / 2) / baudrate; + writel(USART3_BF(CD, divisor), &usart->brgr); +} + +void _atmel_serial_init(atmel_usart3_t *usart, + ulong usart_clk_rate, int baudrate) +{ + writel(USART3_BIT(RXDIS) | USART3_BIT(TXDIS), &usart->cr); + + writel((USART3_BF(USART_MODE, USART3_USART_MODE_NORMAL) | + USART3_BF(USCLKS, USART3_USCLKS_MCK) | + USART3_BF(CHRL, USART3_CHRL_8) | + USART3_BF(PAR, USART3_PAR_NONE) | + USART3_BF(NBSTOP, USART3_NBSTOP_1)), &usart->mr); + + _atmel_serial_set_brg(usart, usart_clk_rate, baudrate); + + writel(USART3_BIT(RSTRX) | USART3_BIT(RSTTX), &usart->cr); + writel(USART3_BIT(RXEN) | USART3_BIT(TXEN), &usart->cr); +} + int atmel_serial_setbrg(struct udevice *dev, int baudrate) { struct atmel_serial_priv *priv = dev_get_priv(dev); - atmel_serial_setbrg_internal(priv->usart, 0 /* ignored */, baudrate); - atmel_serial_activate(priv->usart); + _atmel_serial_set_brg(priv->usart, priv->usart_clk_rate, baudrate); return 0; } @@ -188,10 +219,39 @@ static const struct dm_serial_ops atmel_serial_ops = { .setbrg = atmel_serial_setbrg, }; +static int atmel_serial_enable_clk(struct udevice *dev) +{ + struct atmel_serial_priv *priv = dev_get_priv(dev); + struct clk clk; + ulong clk_rate; + int ret; + + ret = clk_get_by_index(dev, 0, &clk); + if (ret) + return -EINVAL; + + if (dev_get_driver_data(dev) == CLK_TYPE_NORMAL) { + ret = clk_enable(&clk); + if (ret) + return ret; + } + + clk_rate = clk_get_rate(&clk); + if (!clk_rate) + return -EINVAL; + + priv->usart_clk_rate = clk_rate; + + clk_free(&clk); + + return 0; +} + static int atmel_serial_probe(struct udevice *dev) { struct atmel_serial_platdata *plat = dev->platdata; struct atmel_serial_priv *priv = dev_get_priv(dev); + int ret; #if CONFIG_IS_ENABLED(OF_CONTROL) fdt_addr_t addr_base; @@ -202,14 +262,26 @@ static int atmel_serial_probe(struct udevice *dev) plat->base_addr = (uint32_t)addr_base; #endif priv->usart = (atmel_usart3_t *)plat->base_addr; - atmel_serial_init_internal(priv->usart); + + ret = atmel_serial_enable_clk(dev); + if (ret) + return ret; + + _atmel_serial_init(priv->usart, priv->usart_clk_rate, gd->baudrate); return 0; } #if CONFIG_IS_ENABLED(OF_CONTROL) static const struct udevice_id atmel_serial_ids[] = { - { .compatible = "atmel,at91sam9260-usart" }, + { + .compatible = "atmel,at91sam9260-dbgu", + .data = CLK_TYPE_DBGU, + }, + { + .compatible = "atmel,at91sam9260-usart", + .data = CLK_TYPE_NORMAL, + }, { } }; #endif @@ -233,7 +305,7 @@ static inline void _debug_uart_init(void) { atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_DEBUG_UART_BASE; - atmel_serial_setbrg_internal(usart, 0, CONFIG_BAUDRATE); + _atmel_serial_init(usart, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE); } static inline void _debug_uart_putc(int ch) |