diff options
Diffstat (limited to 'drivers')
35 files changed, 1957 insertions, 171 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 1e0246da6d..c6c2eb14db 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -1,5 +1,6 @@ menu "Drivers" +source "drivers/base/Kconfig" source "drivers/efi/Kconfig" source "drivers/of/Kconfig" source "drivers/aiodev/Kconfig" @@ -37,5 +38,6 @@ source "drivers/firmware/Kconfig" source "drivers/phy/Kconfig" source "drivers/crypto/Kconfig" source "drivers/memory/Kconfig" +source "drivers/soc/imx/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 767789d541..752fd66242 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -38,3 +38,4 @@ obj-$(CONFIG_HAB) += hab/ obj-$(CONFIG_CRYPTO_HW) += crypto/ obj-$(CONFIG_AIODEV) += aiodev/ obj-y += memory/ +obj-y += soc/imx/ diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig new file mode 100644 index 0000000000..1e13e5ed9d --- /dev/null +++ b/drivers/base/Kconfig @@ -0,0 +1,3 @@ + +config PM_GENERIC_DOMAINS + bool diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 4bd4217745..6d2cef8e1a 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -2,4 +2,6 @@ obj-y += bus.o obj-y += driver.o obj-y += platform.o obj-y += resource.o -obj-y += regmap/
\ No newline at end of file +obj-y += regmap/ + +obj-$(CONFIG_PM_GENERIC_DOMAINS) += power.o diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 1fd890542e..1fd6bbc014 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -269,7 +269,7 @@ static int device_probe_deferred(void) success = false; if (list_empty(&deferred)) - break; + return 0; list_for_each_entry_safe(dev, tmp, &deferred, active) { list_del(&dev->active); @@ -285,9 +285,6 @@ static int device_probe_deferred(void) } } while (success); - if (list_empty(&deferred)) - return 0; - list_for_each_entry(dev, &deferred, active) dev_err(dev, "probe permanently deferred\n"); diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 85bdfb0149..1d3fa2eb44 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -21,9 +21,16 @@ #include <errno.h> #include <init.h> #include <of.h> +#include <pm_domain.h> static int platform_probe(struct device_d *dev) { + int ret; + + ret = genpd_dev_pm_attach(dev); + if (ret < 0) + return ret; + return dev->driver->probe(dev); } diff --git a/drivers/base/power.c b/drivers/base/power.c new file mode 100644 index 0000000000..12674ca7d9 --- /dev/null +++ b/drivers/base/power.c @@ -0,0 +1,249 @@ +#include <common.h> +#include <driver.h> +#include <errno.h> +#include <of.h> + +#include <pm_domain.h> + +#define genpd_status_on(genpd) (genpd->status == GPD_STATE_ACTIVE) + +static LIST_HEAD(gpd_list); + +/** + * pm_genpd_init - Initialize a generic I/O PM domain object. + * @genpd: PM domain object to initialize. + * @gov: PM domain governor to associate with the domain (may be NULL). + * @is_off: Initial value of the domain's power_is_off field. + * + * Returns 0 on successful initialization, else a negative error code. + */ +int pm_genpd_init(struct generic_pm_domain *genpd, void *gov, bool is_off) +{ + if (IS_ERR_OR_NULL(genpd)) + return -EINVAL; + + genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; + + list_add(&genpd->gpd_list_node, &gpd_list); + + return 0; +} +EXPORT_SYMBOL_GPL(pm_genpd_init); + +/** + * struct of_genpd_provider - PM domain provider registration structure + * @link: Entry in global list of PM domain providers + * @node: Pointer to device tree node of PM domain provider + * @xlate: Provider-specific xlate callback mapping a set of specifier cells + * into a PM domain. + * @data: context pointer to be passed into @xlate callback + */ +struct of_genpd_provider { + struct list_head link; + struct device_node *node; + genpd_xlate_t xlate; + void *data; +}; + +/* List of registered PM domain providers. */ +static LIST_HEAD(of_genpd_providers); + +static bool genpd_present(const struct generic_pm_domain *genpd) +{ + const struct generic_pm_domain *gpd; + + if (IS_ERR_OR_NULL(genpd)) + return false; + + list_for_each_entry(gpd, &gpd_list, gpd_list_node) + if (gpd == genpd) + return true; + + return false; +} + +/** + * genpd_xlate_simple() - Xlate function for direct node-domain mapping + * @genpdspec: OF phandle args to map into a PM domain + * @data: xlate function private data - pointer to struct generic_pm_domain + * + * This is a generic xlate function that can be used to model PM domains that + * have their own device tree nodes. The private data of xlate function needs + * to be a valid pointer to struct generic_pm_domain. + */ +static struct generic_pm_domain *genpd_xlate_simple( + struct of_phandle_args *genpdspec, + void *data) +{ + return data; +} + +/** + * genpd_add_provider() - Register a PM domain provider for a node + * @np: Device node pointer associated with the PM domain provider. + * @xlate: Callback for decoding PM domain from phandle arguments. + * @data: Context pointer for @xlate callback. + */ +static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, + void *data) +{ + struct of_genpd_provider *cp; + + cp = kzalloc(sizeof(*cp), GFP_KERNEL); + if (!cp) + return -ENOMEM; + + cp->node = np; + cp->data = data; + cp->xlate = xlate; + + list_add(&cp->link, &of_genpd_providers); + pr_debug("Added domain provider from %pOF\n", np); + + return 0; +} + +/** + * of_genpd_add_provider_simple() - Register a simple PM domain provider + * @np: Device node pointer associated with the PM domain provider. + * @genpd: Pointer to PM domain associated with the PM domain provider. + */ +int of_genpd_add_provider_simple(struct device_node *np, + struct generic_pm_domain *genpd) +{ + int ret = -EINVAL; + + if (!np || !genpd) + return -EINVAL; + + if (genpd_present(genpd)) + ret = genpd_add_provider(np, genpd_xlate_simple, genpd); + + return ret; +} +EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple); + +/** + * genpd_get_from_provider() - Look-up PM domain + * @genpdspec: OF phandle args to use for look-up + * + * Looks for a PM domain provider under the node specified by @genpdspec and if + * found, uses xlate function of the provider to map phandle args to a PM + * domain. + * + * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR() + * on failure. + */ +static struct generic_pm_domain *genpd_get_from_provider( + struct of_phandle_args *genpdspec) +{ + struct generic_pm_domain *genpd = ERR_PTR(-ENOENT); + struct of_genpd_provider *provider; + + if (!genpdspec) + return ERR_PTR(-EINVAL); + + /* Check if we have such a provider in our array */ + list_for_each_entry(provider, &of_genpd_providers, link) { + if (provider->node == genpdspec->np) + genpd = provider->xlate(genpdspec, provider->data); + if (!IS_ERR(genpd)) + break; + } + + return genpd; +} + +static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) +{ + if (!genpd->power_on) + return 0; + + return genpd->power_on(genpd); +} + +/** + * genpd_power_on - Restore power to a given PM domain and its masters. + * @genpd: PM domain to power up. + * @depth: nesting count for lockdep. + * + * Restore power to @genpd and all of its masters so that it is possible to + * resume a device belonging to it. + */ +static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth) +{ + int ret; + + if (genpd_status_on(genpd)) + return 0; + + ret = _genpd_power_on(genpd, true); + if (ret) + return ret; + + genpd->status = GPD_STATE_ACTIVE; + + return 0; +} + +static int __genpd_dev_pm_attach(struct device_d *dev, struct device_node *np, + unsigned int index, bool power_on) +{ + struct of_phandle_args pd_args; + struct generic_pm_domain *pd; + int ret; + + ret = of_parse_phandle_with_args(np, "power-domains", + "#power-domain-cells", index, &pd_args); + if (ret < 0) + return ret; + + pd = genpd_get_from_provider(&pd_args); + if (IS_ERR(pd)) { + ret = PTR_ERR(pd); + dev_dbg(dev, "%s() failed to find PM domain: %d\n", + __func__, ret); + /* + * Assume that missing genpds are unresolved + * dependency are report them as deferred + */ + return (ret == -ENOENT) ? -EPROBE_DEFER : ret; + } + + dev_dbg(dev, "adding to PM domain %s\n", pd->name); + + if (power_on) + ret = genpd_power_on(pd, 0); + + return ret ?: 1; +} + +/** + * genpd_dev_pm_attach - Attach a device to its PM domain using DT. + * @dev: Device to attach. + * + * Parse device's OF node to find a PM domain specifier. If such is found, + * attaches the device to retrieved pm_domain ops. + * + * Returns 1 on successfully attached PM domain, 0 when the device don't need a + * PM domain or when multiple power-domains exists for it, else a negative error + * code. Note that if a power-domain exists for the device, but it cannot be + * found or turned on, then return -EPROBE_DEFER to ensure that the device is + * not probed and to re-try again later. + */ +int genpd_dev_pm_attach(struct device_d *dev) +{ + if (!dev->device_node) + return 0; + + /* + * Devices with multiple PM domains must be attached separately, as we + * can only attach one PM domain per device. + */ + if (of_count_phandle_with_args(dev->device_node, "power-domains", + "#power-domain-cells") != 1) + return 0; + + return __genpd_dev_pm_attach(dev, dev->device_node, 0, true); +} +EXPORT_SYMBOL_GPL(genpd_dev_pm_attach); diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 202df59762..219982d878 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -6,6 +6,14 @@ config BUS_OMAP_GPMC depends on OMAP_GPMC bool "TI OMAP/AM33xx GPMC support" +config TI_SYSC + depends on ARCH_OMAP + bool "TI sysc interconnect target module driver" + default y + help + Generic driver for Texas Instruments interconnect target module + found on many TI SoCs. + config IMX_WEIM depends on ARCH_IMX bool "i.MX WEIM driver" diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 4b7aa888ab..ba5cee4063 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_BUS_OMAP_GPMC) += omap-gpmc.o obj-$(CONFIG_IMX_WEIM) += imx-weim.o obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o +obj-$(CONFIG_TI_SYSC) += ti-sysc.o diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c new file mode 100644 index 0000000000..af52d839bd --- /dev/null +++ b/drivers/bus/ti-sysc.c @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 Phytec Messtechnik GmbH, Teresa Remmet <t.remmet@phytec.de> + */ + +#include <common.h> +#include <init.h> +#include <of.h> +#include <linux/err.h> + +static int ti_sysc_probe(struct device_d *dev) +{ + int ret; + + ret = of_platform_populate(dev->device_node, + of_default_bus_match_table, dev); + if (ret) + dev_err(dev, "%s fail to create devices.\n", + dev->device_node->full_name); + return ret; +}; + +static struct of_device_id ti_sysc_dt_ids[] = { + { .compatible = "ti,sysc-omap4",}, + { .compatible = "ti,sysc-omap4-simple",}, + { .compatible = "ti,sysc-omap4-timer",}, + { .compatible = "ti,sysc-omap2",}, + { }, +}; + +static struct driver_d ti_sysc_driver = { + .name = "ti-sysc", + .probe = ti_sysc_probe, + .of_compatible = DRV_OF_COMPAT(ti_sysc_dt_ids), +}; + +postcore_platform_driver(ti_sysc_driver); diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index 432004ec9c..65f71c6fce 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -1053,6 +1053,7 @@ static int cpsw_probe_dt(struct cpsw_priv *priv) { struct device_d *dev = priv->dev; struct device_node *np = dev->device_node, *child; + struct device_node *physel; int ret, i = 0; ret = of_property_read_u32(np, "slaves", &priv->num_slaves); @@ -1061,13 +1062,17 @@ static int cpsw_probe_dt(struct cpsw_priv *priv) priv->slaves = xzalloc(sizeof(struct cpsw_slave) * priv->num_slaves); - for_each_child_of_node(np, child) { - if (of_device_is_compatible(child, "ti,am3352-cpsw-phy-sel")) { - ret = cpsw_phy_sel_init(priv, child); - if (ret) - return ret; - } + physel = of_parse_phandle(dev->device_node, "cpsw-phy-sel", 0); + if (!physel) { + physel = of_get_child_by_name(dev->device_node, "cpsw-phy-sel"); + if (!physel) + dev_err(dev, "Phy mode node not found\n"); + } + ret = cpsw_phy_sel_init(priv, physel); + if (ret) + return ret; + for_each_child_of_node(np, child) { if (of_device_is_compatible(child, "ti,davinci_mdio")) { ret = of_pinctrl_select_state_default(child); if (ret) diff --git a/drivers/net/designware.c b/drivers/net/designware.c index ad70967e8c..3c9bca981c 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -291,12 +291,14 @@ static int dwc_ether_send(struct eth_device *dev, void *packet, int length) if (priv->enh_desc) { desc_p->txrx_status |= DESC_ENH_TXSTS_TXFIRST | DESC_ENH_TXSTS_TXLAST; + desc_p->dmamac_cntl &= ~(DESC_ENH_TXCTRL_SIZE1MASK); desc_p->dmamac_cntl |= (length << DESC_ENH_TXCTRL_SIZE1SHFT) & DESC_ENH_TXCTRL_SIZE1MASK; desc_p->txrx_status &= ~(DESC_ENH_TXSTS_MSK); desc_p->txrx_status |= DESC_ENH_TXSTS_OWNBYDMA; } else { + desc_p->dmamac_cntl &= ~(DESC_TXCTRL_SIZE1MASK); desc_p->dmamac_cntl |= ((length << DESC_TXCTRL_SIZE1SHFT) & DESC_TXCTRL_SIZE1MASK) | DESC_TXCTRL_TXLAST | DESC_TXCTRL_TXFIRST; diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h index 0a9e107c07..52ad3d4cdb 100644 --- a/drivers/net/e1000/e1000.h +++ b/drivers/net/e1000/e1000.h @@ -2182,6 +2182,7 @@ struct e1000_hw { struct e1000_tx_desc *tx_base; struct e1000_rx_desc *rx_base; unsigned char *packet; + dma_addr_t packet_dma; int tx_tail; int rx_tail, rx_last; diff --git a/drivers/net/e1000/eeprom.c b/drivers/net/e1000/eeprom.c index 01f6addbc3..5b34e9b8d1 100644 --- a/drivers/net/e1000/eeprom.c +++ b/drivers/net/e1000/eeprom.c @@ -1000,7 +1000,8 @@ int32_t e1000_read_eeprom(struct e1000_hw *hw, uint16_t offset, (words > eeprom->word_size - offset) || (words == 0)) { dev_dbg(hw->dev, "\"words\" parameter out of bounds." - "Words = %d, size = %d\n", offset, eeprom->word_size); + "Words = %d, size = %d\n", offset, + (int)eeprom->word_size); return -E1000_ERR_EEPROM; } diff --git a/drivers/net/e1000/main.c b/drivers/net/e1000/main.c index 0ef8fd6231..774e3d030f 100644 --- a/drivers/net/e1000/main.c +++ b/drivers/net/e1000/main.c @@ -33,13 +33,10 @@ tested on both gig copper and gig fiber boards #include <init.h> #include <malloc.h> #include <linux/pci.h> +#include <linux/iopoll.h> #include <dma.h> #include "e1000.h" - -static u32 inline virt_to_bus(struct pci_dev *pdev, void *adr) -{ - return (u32)adr; -} +#include <io-64-nonatomic-lo-hi.h> #define PCI_VENDOR_ID_INTEL 0x8086 @@ -3201,21 +3198,24 @@ static int e1000_sw_init(struct eth_device *edev) return E1000_SUCCESS; } -static void fill_rx(struct e1000_hw *hw) +static int e1000_bd_next_index(int index) { - volatile struct e1000_rx_desc *rd; - volatile u32 *bla; - int i; + return (index + 1) % 8; +} - hw->rx_last = hw->rx_tail; - rd = hw->rx_base + hw->rx_tail; - hw->rx_tail = (hw->rx_tail + 1) % 8; +static void e1000_fill_rx(struct e1000_hw *hw) +{ + struct e1000_rx_desc *rd = &hw->rx_base[hw->rx_tail]; - bla = (void *)rd; - for (i = 0; i < 4; i++) - *bla++ = 0; + hw->rx_last = hw->rx_tail; + hw->rx_tail = e1000_bd_next_index(hw->rx_tail); - rd->buffer_addr = cpu_to_le64((unsigned long)hw->packet); + writeq(hw->packet_dma, &rd->buffer_addr); + writew(0, &rd->length); + writew(0, &rd->csum); + writeb(0, &rd->status); + writeb(0, &rd->errors); + writew(0, &rd->special); e1000_write_reg(hw, E1000_RDT, hw->rx_tail); } @@ -3232,9 +3232,10 @@ static void e1000_configure_tx(struct e1000_hw *hw) unsigned long tctl; unsigned long tipg, tarc; uint32_t ipgr1, ipgr2; + const unsigned long tx_base = (unsigned long)hw->tx_base; - e1000_write_reg(hw, E1000_TDBAL, (unsigned long)hw->tx_base); - e1000_write_reg(hw, E1000_TDBAH, 0); + e1000_write_reg(hw, E1000_TDBAL, lower_32_bits(tx_base)); + e1000_write_reg(hw, E1000_TDBAH, upper_32_bits(tx_base)); e1000_write_reg(hw, E1000_TDLEN, 128); @@ -3350,6 +3351,7 @@ static void e1000_setup_rctl(struct e1000_hw *hw) static void e1000_configure_rx(struct e1000_hw *hw) { unsigned long rctl, ctrl_ext; + const unsigned long rx_base = (unsigned long)hw->rx_base; hw->rx_tail = 0; /* make sure receives are disabled while setting up the descriptors */ @@ -3371,8 +3373,8 @@ static void e1000_configure_rx(struct e1000_hw *hw) e1000_write_flush(hw); } /* Setup the Base and Length of the Rx Descriptor Ring */ - e1000_write_reg(hw, E1000_RDBAL, (unsigned long)hw->rx_base); - e1000_write_reg(hw, E1000_RDBAH, 0); + e1000_write_reg(hw, E1000_RDBAL, lower_32_bits(rx_base)); + e1000_write_reg(hw, E1000_RDBAH, upper_32_bits(rx_base)); e1000_write_reg(hw, E1000_RDLEN, 128); @@ -3390,59 +3392,62 @@ static void e1000_configure_rx(struct e1000_hw *hw) e1000_write_reg(hw, E1000_RCTL, rctl); - fill_rx(hw); + e1000_fill_rx(hw); } static int e1000_poll(struct eth_device *edev) { struct e1000_hw *hw = edev->priv; - volatile struct e1000_rx_desc *rd; - uint32_t len; + struct e1000_rx_desc *rd = &hw->rx_base[hw->rx_last]; - rd = hw->rx_base + hw->rx_last; + if (readb(&rd->status) & E1000_RXD_STAT_DD) { + const uint16_t len = readw(&rd->length); - if (!(le32_to_cpu(rd->status)) & E1000_RXD_STAT_DD) - return 0; + dma_sync_single_for_cpu(hw->packet_dma, len, + DMA_FROM_DEVICE); - len = le32_to_cpu(rd->length); + net_receive(edev, hw->packet, len); - dma_sync_single_for_cpu((unsigned long)hw->packet, len, DMA_FROM_DEVICE); + dma_sync_single_for_device(hw->packet_dma, len, + DMA_FROM_DEVICE); + e1000_fill_rx(hw); + return 1; + } - net_receive(edev, (uchar *)hw->packet, len); - fill_rx(hw); - return 1; + return 0; } static int e1000_transmit(struct eth_device *edev, void *txpacket, int length) { struct e1000_hw *hw = edev->priv; - volatile struct e1000_tx_desc *txp; - uint64_t to; + struct e1000_tx_desc *txp = &hw->tx_base[hw->tx_tail]; + dma_addr_t dma; + uint32_t stat; + int ret; - txp = hw->tx_base + hw->tx_tail; - hw->tx_tail = (hw->tx_tail + 1) % 8; + hw->tx_tail = e1000_bd_next_index(hw->tx_tail); - txp->buffer_addr = cpu_to_le64(virt_to_bus(hw->pdev, txpacket)); - txp->lower.data = cpu_to_le32(hw->txd_cmd | length); - txp->upper.data = 0; + writel(hw->txd_cmd | length, &txp->lower.data); + writel(0, &txp->upper.data); - dma_sync_single_for_device((unsigned long)txpacket, length, DMA_TO_DEVICE); + dma = dma_map_single(hw->dev, txpacket, length, DMA_TO_DEVICE); + if (dma_mapping_error(hw->dev, dma)) + return -EFAULT; + writeq(dma, &txp->buffer_addr); e1000_write_reg(hw, E1000_TDT, hw->tx_tail); e1000_write_flush(hw); - to = get_time_ns(); - while (1) { - if (le32_to_cpu(txp->upper.data) & E1000_TXD_STAT_DD) - break; - if (is_timeout(to, MSECOND)) { - dev_dbg(hw->dev, "e1000: tx timeout\n"); - return -ETIMEDOUT; - } - } + ret = readl_poll_timeout(&txp->upper.data, + stat, stat & E1000_TXD_STAT_DD, + MSECOND / USECOND); + if (ret) + dev_dbg(hw->dev, "e1000: tx timeout\n"); - return 0; + dma_unmap_single(hw->dev, dma, length, DMA_TO_DEVICE); + + return ret; } static void e1000_disable(struct eth_device *edev) @@ -3561,7 +3566,6 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *id) hw->tx_base = dma_alloc_coherent(16 * sizeof(*hw->tx_base), DMA_ADDRESS_BROKEN); hw->rx_base = dma_alloc_coherent(16 * sizeof(*hw->rx_base), DMA_ADDRESS_BROKEN); - hw->packet = dma_alloc_coherent(4096, DMA_ADDRESS_BROKEN); edev = &hw->edev; @@ -3570,6 +3574,15 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *id) pdev->dev.priv = hw; edev->priv = hw; + hw->packet = dma_alloc(PAGE_SIZE); + if (!hw->packet) + return -ENOMEM; + + hw->packet_dma = dma_map_single(hw->dev, hw->packet, PAGE_SIZE, + DMA_FROM_DEVICE); + if (dma_mapping_error(hw->dev, hw->packet_dma)) + return -EFAULT; + hw->hw_addr = pci_iomap(pdev, 0); /* MAC and Phy settings */ diff --git a/drivers/net/fec_imx.c b/drivers/net/fec_imx.c index 4823b08340..38a29fc83e 100644 --- a/drivers/net/fec_imx.c +++ b/drivers/net/fec_imx.c @@ -395,6 +395,13 @@ static void fec_halt(struct eth_device *dev) struct fec_priv *fec = (struct fec_priv *)dev->priv; uint32_t reg; + /* + * Only halt if fec has been started. Otherwise we would have to wait + * for the timeout below. + */ + if (!(readl(fec->regs + FEC_ECNTRL) & FEC_ECNTRL_ETHER_EN)) + return; + /* issue graceful stop command to the FEC transmitter if necessary */ writel(readl(fec->regs + FEC_X_CNTRL) | FEC_ECNTRL_RESET, fec->regs + FEC_X_CNTRL); diff --git a/drivers/of/platform.c b/drivers/of/platform.c index ef8969ca8b..d3795d799a 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -207,6 +207,25 @@ struct device_d *of_device_enable_and_register_by_name(const char *name) } EXPORT_SYMBOL(of_device_enable_and_register_by_name); +/** + * of_device_enable_and_register_by_alias - Enable and register device by alias + * @name: alias of the device node + * + * Returns pointer to created platform device, or NULL if a device was not + * registered. Unavailable devices will not get registered. + */ +struct device_d *of_device_enable_and_register_by_alias(const char *alias) +{ + struct device_node *node; + + node = of_find_node_by_alias(NULL, alias); + if (!node) + return NULL; + + return of_device_enable_and_register(node); +} +EXPORT_SYMBOL(of_device_enable_and_register_by_alias); + #ifdef CONFIG_ARM_AMBA static struct device_d *of_amba_device_create(struct device_node *np) { diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 1c45a1c225..44a89d005f 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -41,8 +41,8 @@ config PCI_TEGRA select PCI config PCI_IMX6 - bool "Freescale i.MX6 PCIe controller" - depends on ARCH_IMX6 + bool "Freescale i.MX6/7/8 PCIe controller" + depends on ARCH_IMX6 || ARCH_IMX7 || ARCH_IMX8MQ select PCIE_DW select OF_PCI select PCI diff --git a/drivers/pci/pci-imx6.c b/drivers/pci/pci-imx6.c index 38e002a1c6..138b4ca8b3 100644 --- a/drivers/pci/pci-imx6.c +++ b/drivers/pci/pci-imx6.c @@ -26,17 +26,37 @@ #include <linux/phy/phy.h> #include <linux/reset.h> #include <linux/sizes.h> +#include <linux/bitfield.h> #include <mfd/imx6q-iomuxc-gpr.h> +#include <mfd/imx7-iomuxc-gpr.h> #include <mach/imx6-regs.h> +#include <mach/imx7-regs.h> +#include <mach/imx8mq-regs.h> #include "pcie-designware.h" +#define IMX8MQ_GPR_PCIE_REF_USE_PAD BIT(9) +#define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN BIT(10) +#define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE BIT(11) +#define IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE GENMASK(11, 8) +#define IMX8MQ_PCIE2_BASE_ADDR 0x33c00000 + #define to_imx6_pcie(x) ((x)->dev->priv) enum imx6_pcie_variants { IMX6Q, IMX6QP, + IMX7D, + IMX8MQ, +}; + +#define IMX6_PCIE_FLAG_IMX6_PHY BIT(0) +#define IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE BIT(1) + +struct imx6_pcie_drvdata { + enum imx6_pcie_variants variant; + u32 flags; }; struct imx6_pcie { @@ -46,15 +66,23 @@ struct imx6_pcie { struct clk *pcie_phy; struct clk *pcie; void __iomem *iomuxc_gpr; - enum imx6_pcie_variants variant; + u32 controller_id; + struct reset_control *pciephy_reset; + struct reset_control *apps_reset; u32 tx_deemph_gen1; u32 tx_deemph_gen2_3p5db; u32 tx_deemph_gen2_6db; u32 tx_swing_full; u32 tx_swing_low; int link_gen; + const struct imx6_pcie_drvdata *drvdata; }; +/* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */ +#define PHY_PLL_LOCK_WAIT_MAX_RETRIES 2000 +#define PHY_PLL_LOCK_WAIT_USLEEP_MIN 50 +#define PHY_PLL_LOCK_WAIT_USLEEP_MAX 200 + /* PCIe Root Complex registers (memory-mapped) */ #define PCIE_RC_LCR 0x7c #define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1 0x1 @@ -221,6 +249,9 @@ static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie) { uint32_t temp; + if (!(imx6_pcie->drvdata->flags & IMX6_PCIE_FLAG_IMX6_PHY)) + return; + pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &temp); temp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN | PHY_RX_OVRD_IN_LO_RX_PLL_EN); @@ -238,7 +269,12 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) { u32 gpr1; - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { + case IMX7D: + case IMX8MQ: + reset_control_assert(imx6_pcie->pciephy_reset); + reset_control_assert(imx6_pcie->apps_reset); + break; case IMX6QP: gpr1 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); gpr1 |= IMX6Q_GPR1_PCIE_SW_RST; @@ -255,28 +291,74 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) } } +static unsigned int imx6_pcie_grp_offset(const struct imx6_pcie *imx6_pcie) +{ + WARN_ON(imx6_pcie->drvdata->variant != IMX8MQ); + return imx6_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14; +} + static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) { - u32 gpr1; + u32 gpr1, gpr1x; + unsigned int offset; - /* power up core phy and enable ref clock */ - gpr1 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); - gpr1 &= ~IMX6Q_GPR1_PCIE_TEST_PD; - writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); - /* - * the async reset input need ref clock to sync internally, - * when the ref clock comes after reset, internal synced - * reset time is too short, cannot meet the requirement. - * add one ~10us delay here. - */ - udelay(10); - gpr1 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); - gpr1 |= IMX6Q_GPR1_PCIE_REF_CLK_EN; - writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); + switch (imx6_pcie->drvdata->variant) { + case IMX6QP: + case IMX6Q: /* FALLTHROUGH */ + /* power up core phy and enable ref clock */ + gpr1 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); + gpr1 &= ~IMX6Q_GPR1_PCIE_TEST_PD; + writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); + /* + * the async reset input need ref clock to sync + * internally, when the ref clock comes after reset, + * internal synced reset time is too short, cannot + * meet the requirement. add one ~10us delay here. + */ + udelay(10); + gpr1 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); + gpr1 |= IMX6Q_GPR1_PCIE_REF_CLK_EN; + writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); + break; + case IMX7D: + break; + case IMX8MQ: + offset = imx6_pcie_grp_offset(imx6_pcie); + /* + * Set the over ride low and enabled + * make sure that REF_CLK is turned on. + */ + gpr1x = readl(imx6_pcie->iomuxc_gpr + offset); + gpr1x &= ~IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE; + writel(gpr1x, imx6_pcie->iomuxc_gpr + offset); + + gpr1x = readl(imx6_pcie->iomuxc_gpr + offset); + gpr1x |= IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN; + writel(gpr1x, imx6_pcie->iomuxc_gpr + offset); + break; + } return 0; } +static void imx7d_pcie_wait_for_phy_pll_lock(struct imx6_pcie *imx6_pcie) +{ + u32 val; + unsigned int retries; + struct device_d *dev = imx6_pcie->pci->dev; + + for (retries = 0; retries < PHY_PLL_LOCK_WAIT_MAX_RETRIES; retries++) { + val = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR22); + + if (val & IMX7D_GPR22_PCIE_PHY_PLL_LOCKED) + return; + + udelay(PHY_PLL_LOCK_WAIT_USLEEP_MAX); + } + + dev_err(dev, "PCIe PLL lock timeout\n"); +} + static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) { struct device_d *dev = imx6_pcie->pci->dev; @@ -320,7 +402,14 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) /* * Release the PCIe PHY reset here */ - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { + case IMX8MQ: + reset_control_deassert(imx6_pcie->pciephy_reset); + break; + case IMX7D: + reset_control_deassert(imx6_pcie->pciephy_reset); + imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie); + break; case IMX6QP: gpr1 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); gpr1 &= ~IMX6Q_GPR1_PCIE_SW_RST; @@ -342,43 +431,84 @@ err_pcie_bus: clk_disable(imx6_pcie->pcie_phy); } -static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) +static void imx6_pcie_configure_type(struct imx6_pcie *imx6_pcie) { - u32 gpr12, gpr8; + unsigned int mask, val; + u32 gpr12; - gpr12 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); - gpr12 &= ~IMX6Q_GPR12_PCIE_CTL_2; - writel(gpr12, imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); + if (imx6_pcie->drvdata->variant == IMX8MQ && + imx6_pcie->controller_id == 1) { + mask = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE; + val = FIELD_PREP(IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE, + PCI_EXP_TYPE_ROOT_PORT); + } else { + mask = IMX6Q_GPR12_DEVICE_TYPE; + val = FIELD_PREP(IMX6Q_GPR12_DEVICE_TYPE, + PCI_EXP_TYPE_ROOT_PORT); + } - /* configure constant input signal to the pcie ctrl and phy */ - gpr12 &= ~IMX6Q_GPR12_DEVICE_TYPE; - gpr12 |= PCI_EXP_TYPE_ROOT_PORT << 12; + gpr12 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); + gpr12 &= ~mask; + gpr12 |= val; writel(gpr12, imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); +} - gpr12 &= ~IMX6Q_GPR12_LOS_LEVEL; - gpr12 |= 9 << 4; - writel(gpr12, imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); +static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) +{ + u32 gpr12, gpr8, gpr1x; + unsigned int offset; - gpr8 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR8); - gpr8 &= ~IMX6Q_GPR8_TX_DEEMPH_GEN1; - gpr8 |= imx6_pcie->tx_deemph_gen1 << 0; - writel(gpr8, imx6_pcie->iomuxc_gpr + IOMUXC_GPR8); + switch (imx6_pcie->drvdata->variant) { + case IMX8MQ: + offset = imx6_pcie_grp_offset(imx6_pcie); + /* + * TODO: Currently this code assumes external + * oscillator is being used + */ + gpr1x = readl(imx6_pcie->iomuxc_gpr + offset); + gpr1x |= IMX8MQ_GPR_PCIE_REF_USE_PAD; + writel(gpr1x, imx6_pcie->iomuxc_gpr + offset); + break; + case IMX7D: + gpr12 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); + gpr12 &= ~IMX7D_GPR12_PCIE_PHY_REFCLK_SEL; + writel(gpr12, imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); + break; + case IMX6QP: + case IMX6Q: /* FALLTHROUGH */ + gpr12 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); + gpr12 &= ~IMX6Q_GPR12_PCIE_CTL_2; + writel(gpr12, imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); - gpr8 &= ~IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB; - gpr8 |= imx6_pcie->tx_deemph_gen2_3p5db << 6; - writel(gpr8, imx6_pcie->iomuxc_gpr + IOMUXC_GPR8); + /* configure constant input signal to the pcie ctrl and phy */ + gpr12 &= ~IMX6Q_GPR12_LOS_LEVEL; + gpr12 |= 9 << 4; + writel(gpr12, imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); - gpr8 &= ~IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB; - gpr8 |= imx6_pcie->tx_deemph_gen2_6db << 12; - writel(gpr8, imx6_pcie->iomuxc_gpr + IOMUXC_GPR8); + gpr8 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR8); + gpr8 &= ~IMX6Q_GPR8_TX_DEEMPH_GEN1; + gpr8 |= imx6_pcie->tx_deemph_gen1 << 0; + writel(gpr8, imx6_pcie->iomuxc_gpr + IOMUXC_GPR8); - gpr8 &= ~IMX6Q_GPR8_TX_SWING_FULL; - gpr8 |= imx6_pcie->tx_swing_full << 18; - writel(gpr8, imx6_pcie->iomuxc_gpr + IOMUXC_GPR8); + gpr8 &= ~IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB; + gpr8 |= imx6_pcie->tx_deemph_gen2_3p5db << 6; + writel(gpr8, imx6_pcie->iomuxc_gpr + IOMUXC_GPR8); - gpr8 &= ~IMX6Q_GPR8_TX_SWING_LOW; - gpr8 |= imx6_pcie->tx_swing_low << 25; - writel(gpr8, imx6_pcie->iomuxc_gpr + IOMUXC_GPR8); + gpr8 &= ~IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB; + gpr8 |= imx6_pcie->tx_deemph_gen2_6db << 12; + writel(gpr8, imx6_pcie->iomuxc_gpr + IOMUXC_GPR8); + + gpr8 &= ~IMX6Q_GPR8_TX_SWING_FULL; + gpr8 |= imx6_pcie->tx_swing_full << 18; + writel(gpr8, imx6_pcie->iomuxc_gpr + IOMUXC_GPR8); + + gpr8 &= ~IMX6Q_GPR8_TX_SWING_LOW; + gpr8 |= imx6_pcie->tx_swing_low << 25; + writel(gpr8, imx6_pcie->iomuxc_gpr + IOMUXC_GPR8); + break; + } + + imx6_pcie_configure_type(imx6_pcie); } static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie) @@ -404,6 +534,24 @@ static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie) return -EINVAL; } +static void imx6_pcie_ltssm_enable(struct device_d *dev) +{ + struct imx6_pcie *imx6_pcie = dev->priv; + u32 gpr12; + + switch (imx6_pcie->drvdata->variant) { + case IMX6Q: + case IMX6QP: + gpr12 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); + gpr12 |= IMX6Q_GPR12_PCIE_CTL_2; + writel(gpr12, imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); + break; + case IMX7D: + case IMX8MQ: + reset_control_deassert(imx6_pcie->apps_reset); + break; + } +} static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) { @@ -411,7 +559,6 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) struct device_d *dev = pci->dev; uint32_t tmp; int ret; - u32 gpr12; /* * Force Gen1 operation when starting the link. In case the link is @@ -423,10 +570,7 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1; dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp); - /* Start LTSSM. */ - gpr12 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); - gpr12 |= IMX6Q_GPR12_PCIE_CTL_2; - writel(gpr12, imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); + imx6_pcie_ltssm_enable(dev); ret = imx6_pcie_wait_for_link(imx6_pcie); if (ret) @@ -439,30 +583,43 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK; tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2; dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp); - } else { - dev_info(dev, "Link: Gen2 disabled\n"); - } - /* - * Start Directed Speed Change so the best possible speed both link - * partners support can be negotiated. - */ - tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); - tmp |= PORT_LOGIC_SPEED_CHANGE; - dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp); - - ret = imx6_pcie_wait_for_speed_change(imx6_pcie); - if (ret) { - dev_err(dev, "Failed to bring link up!\n"); - goto err_reset_phy; - } + /* + * Start Directed Speed Change so the best possible + * speed both link partners support can be negotiated. + */ + tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); + tmp |= PORT_LOGIC_SPEED_CHANGE; + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp); + + if (imx6_pcie->drvdata->flags & + IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE) { + /* + * On i.MX7, DIRECT_SPEED_CHANGE behaves + * differently from i.MX6 family when no link + * speed transition occurs and we go Gen1 -> + * yep, Gen1. The difference is that, in such + * case, it will not be cleared by HW which + * will cause the following code to report + * false failure. + */ + + ret = imx6_pcie_wait_for_speed_change(imx6_pcie); + if (ret) { + dev_err(dev, "Failed to bring link up!\n"); + goto err_reset_phy; + } + } - /* Make sure link training is finished as well! */ - ret = imx6_pcie_wait_for_link(imx6_pcie); - if (ret) { - dev_err(dev, "Failed to bring link up!\n"); - goto err_reset_phy; - } + /* Make sure link training is finished as well! */ + ret = imx6_pcie_wait_for_link(imx6_pcie); + if (ret) { + dev_err(dev, "Failed to bring link up!\n"); + goto err_reset_phy; + } + } else { + dev_info(dev, "Link: Gen2 disabled\n"); + } tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCSR); dev_info(dev, "Link up, Gen%i\n", (tmp >> 16) & 0xf); @@ -505,14 +662,13 @@ static const struct dw_pcie_host_ops imx6_pcie_host_ops = { .host_init = imx6_pcie_host_init, }; -static int __init imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, - struct device_d *dev) +static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, + struct device_d *dev) { struct dw_pcie *pci = imx6_pcie->pci; struct pcie_port *pp = &pci->pp; int ret; - pp->root_bus_nr = -1; pp->ops = &imx6_pcie_host_ops; ret = dw_pcie_host_init(pp); @@ -524,7 +680,7 @@ static int __init imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, return 0; } -static int __init imx6_pcie_probe(struct device_d *dev) +static int imx6_pcie_probe(struct device_d *dev) { struct resource *iores; struct dw_pcie *pci; @@ -539,16 +695,13 @@ static int __init imx6_pcie_probe(struct device_d *dev) pci->ops = &dw_pcie_ops; imx6_pcie->pci = pci; - imx6_pcie->variant = - (enum imx6_pcie_variants)of_device_get_match_data(dev); - - iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); - pci->dbi_base = IOMEM(iores->start); + imx6_pcie->drvdata = of_device_get_match_data(dev); /* Fetch GPIOs */ imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (imx6_pcie->reset_gpio == -EPROBE_DEFER) + return imx6_pcie->reset_gpio; + if (gpio_is_valid(imx6_pcie->reset_gpio)) { ret = gpio_request_one(imx6_pcie->reset_gpio, GPIOF_OUT_INIT_LOW, "PCIe reset"); @@ -558,6 +711,11 @@ static int __init imx6_pcie_probe(struct device_d *dev) } } + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + pci->dbi_base = IOMEM(iores->start); + /* Fetch clocks */ imx6_pcie->pcie_phy = clk_get(dev, "pcie_phy"); if (IS_ERR(imx6_pcie->pcie_phy)) { @@ -577,8 +735,35 @@ static int __init imx6_pcie_probe(struct device_d *dev) return PTR_ERR(imx6_pcie->pcie); } - /* Grab GPR config register range */ - imx6_pcie->iomuxc_gpr = IOMEM(MX6_IOMUXC_BASE_ADDR); + + switch (imx6_pcie->drvdata->variant) { + case IMX8MQ: + imx6_pcie->iomuxc_gpr = IOMEM(MX8MQ_IOMUXC_GPR_BASE_ADDR); + if (iores->start == IMX8MQ_PCIE2_BASE_ADDR) + imx6_pcie->controller_id = 1; + + goto imx7d_init; + case IMX7D: + imx6_pcie->iomuxc_gpr = IOMEM(MX7_IOMUXC_GPR_BASE_ADDR); + imx7d_init: + imx6_pcie->pciephy_reset = reset_control_get(dev, "pciephy"); + if (IS_ERR(imx6_pcie->pciephy_reset)) { + dev_err(dev, "Failed to get PCIEPHY reset control\n"); + return PTR_ERR(imx6_pcie->pciephy_reset); + } + + imx6_pcie->apps_reset = reset_control_get(dev, "apps"); + if (IS_ERR(imx6_pcie->apps_reset)) { + dev_err(dev, "Failed to get PCIE APPS reset control\n"); + return PTR_ERR(imx6_pcie->apps_reset); + } + break; + default: + /* Grab GPR config register range */ + imx6_pcie->iomuxc_gpr = IOMEM(MX6_IOMUXC_BASE_ADDR); + break; + } + /* Grab PCIe PHY Tx Settings */ if (of_property_read_u32(np, "fsl,tx-deemph-gen1", @@ -620,7 +805,7 @@ static void imx6_pcie_remove(struct device_d *dev) { struct imx6_pcie *imx6_pcie = dev->priv; - if (imx6_pcie->variant == IMX6Q) { + if (imx6_pcie->drvdata->variant == IMX6Q) { /* * If the bootloader already enabled the link we need * some special handling to get the core back into a @@ -650,9 +835,30 @@ static void imx6_pcie_remove(struct device_d *dev) imx6_pcie_assert_core_reset(imx6_pcie); } +static const struct imx6_pcie_drvdata drvdata[] = { + [IMX6Q] = { + .variant = IMX6Q, + .flags = IMX6_PCIE_FLAG_IMX6_PHY | + IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE, + }, + [IMX6QP] = { + .variant = IMX6QP, + .flags = IMX6_PCIE_FLAG_IMX6_PHY | + IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE, + }, + [IMX7D] = { + .variant = IMX7D, + }, + [IMX8MQ] = { + .variant = IMX8MQ, + }, +}; + static struct of_device_id imx6_pcie_of_match[] = { - { .compatible = "fsl,imx6q-pcie", .data = (void *)IMX6Q, }, - { .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, }, + { .compatible = "fsl,imx6q-pcie", .data = &drvdata[IMX6Q], }, + { .compatible = "fsl,imx6qp-pcie", .data = &drvdata[IMX6QP], }, + { .compatible = "fsl,imx7d-pcie", .data = &drvdata[IMX7D], }, + { .compatible = "fsl,imx8mq-pcie", .data = &drvdata[IMX8MQ], } , {}, }; diff --git a/drivers/pci/pcie-designware-host.c b/drivers/pci/pcie-designware-host.c index 6cc4b93a31..dc38cdc8f6 100644 --- a/drivers/pci/pcie-designware-host.c +++ b/drivers/pci/pcie-designware-host.c @@ -150,15 +150,15 @@ int __init dw_pcie_host_init(struct pcie_port *pp) } if (!pci->dbi_base) - pci->dbi_base = (void __force *)pp->cfg.start; + pci->dbi_base = IOMEM(pp->cfg.start); pp->mem_base = pp->mem.start; if (!pp->va_cfg0_base) - pp->va_cfg0_base = (void __force *)(u32)pp->cfg0_base; + pp->va_cfg0_base = IOMEM((unsigned long)pp->cfg0_base); if (!pp->va_cfg1_base) - pp->va_cfg1_base = (void __force *)(u32)pp->cfg1_base; + pp->va_cfg1_base = IOMEM((unsigned long)pp->cfg1_base); ret = of_property_read_u32(np, "num-viewport", &pci->num_viewport); if (ret) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 92db8dc0e0..c734ef5ef9 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -21,4 +21,12 @@ config REGULATOR_PFUZE depends on I2C depends on ARCH_IMX6 +config REGULATOR_ANATOP + tristate "Freescale i.MX on-chip ANATOP LDO regulators" + depends on MFD_SYSCON + help + Say y here to support Freescale i.MX on-chip ANATOP LDOs + regulators. It is recommended that this option be + enabled on i.MX6 platform. + endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index ff5daf9a7d..b2fc5b79b6 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -1,4 +1,5 @@ -obj-$(CONFIG_REGULATOR) += core.o +obj-$(CONFIG_REGULATOR) += core.o helpers.o obj-$(CONFIG_REGULATOR_FIXED) += fixed.o obj-$(CONFIG_REGULATOR_BCM283X) += bcm2835.o -obj-$(CONFIG_REGULATOR_PFUZE) += pfuze.o
\ No newline at end of file +obj-$(CONFIG_REGULATOR_PFUZE) += pfuze.o +obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o
\ No newline at end of file diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c new file mode 100644 index 0000000000..7ec9446a0a --- /dev/null +++ b/drivers/regulator/anatop-regulator.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <common.h> +#include <init.h> +#include <mfd/syscon.h> +#include <regmap.h> +#include <regulator.h> + +struct anatop_regulator { + u32 control_reg; + struct regmap *anatop; + int vol_bit_shift; + int vol_bit_width; + u32 delay_reg; + int delay_bit_shift; + int delay_bit_width; + int min_bit_val; + int min_voltage; + int max_voltage; + + struct regulator_dev rdev; + struct regulator_desc rdesc; + + bool bypass; + int sel; +}; + +static struct regulator_ops anatop_rops = { + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, +}; + +static int anatop_regulator_probe(struct device_d *dev) +{ + struct device_node *np = dev->device_node; + struct device_node *anatop_np; + struct regulator_desc *rdesc; + struct regulator_dev *rdev; + struct anatop_regulator *sreg; + int ret = 0; + + sreg = xzalloc(sizeof(*sreg)); + rdesc = &sreg->rdesc; + rdev = &sreg->rdev; + + anatop_np = of_get_parent(np); + if (!anatop_np) + return -ENODEV; + + rdev->desc = rdesc; + rdev->regmap = syscon_node_to_regmap(anatop_np); + if (IS_ERR(rdev->regmap)) + return PTR_ERR(rdev->regmap); + + ret = of_property_read_u32(np, "anatop-reg-offset", + &sreg->control_reg); + if (ret) { + dev_err(dev, "no anatop-reg-offset property set\n"); + return ret; + } + ret = of_property_read_u32(np, "anatop-vol-bit-width", + &sreg->vol_bit_width); + if (ret) { + dev_err(dev, "no anatop-vol-bit-width property set\n"); + return ret; + } + ret = of_property_read_u32(np, "anatop-vol-bit-shift", + &sreg->vol_bit_shift); + if (ret) { + dev_err(dev, "no anatop-vol-bit-shift property set\n"); + return ret; + } + ret = of_property_read_u32(np, "anatop-min-bit-val", + &sreg->min_bit_val); + if (ret) { + dev_err(dev, "no anatop-min-bit-val property set\n"); + return ret; + } + ret = of_property_read_u32(np, "anatop-min-voltage", + &sreg->min_voltage); + if (ret) { + dev_err(dev, "no anatop-min-voltage property set\n"); + return ret; + } + ret = of_property_read_u32(np, "anatop-max-voltage", + &sreg->max_voltage); + if (ret) { + dev_err(dev, "no anatop-max-voltage property set\n"); + return ret; + } + + /* read LDO ramp up setting, only for core reg */ + of_property_read_u32(np, "anatop-delay-reg-offset", + &sreg->delay_reg); + of_property_read_u32(np, "anatop-delay-bit-width", + &sreg->delay_bit_width); + of_property_read_u32(np, "anatop-delay-bit-shift", + &sreg->delay_bit_shift); + + rdesc->n_voltages = (sreg->max_voltage - sreg->min_voltage) / 25000 + 1 + + sreg->min_bit_val; + rdesc->min_uV = sreg->min_voltage; + rdesc->uV_step = 25000; + rdesc->linear_min_sel = sreg->min_bit_val; + rdesc->vsel_reg = sreg->control_reg; + rdesc->vsel_mask = GENMASK(sreg->vol_bit_width + sreg->vol_bit_shift, + sreg->vol_bit_shift); + + /* Only core regulators have the ramp up delay configuration. */ + if (sreg->control_reg && sreg->delay_bit_width) { + free(sreg); + /* FIXME: This case is not supported */ + return 0; + } else { + u32 enable_bit; + + rdesc->ops = &anatop_rops; + + if (!of_property_read_u32(np, "anatop-enable-bit", + &enable_bit)) { + anatop_rops.enable = regulator_enable_regmap; + anatop_rops.disable = regulator_disable_regmap; + anatop_rops.is_enabled = regulator_is_enabled_regmap; + + rdesc->enable_reg = sreg->control_reg; + rdesc->enable_mask = BIT(enable_bit); + } + } + + return of_regulator_register(rdev, dev->device_node); +} + +static const struct of_device_id of_anatop_regulator_match_tbl[] = { + { .compatible = "fsl,anatop-regulator", }, + { /* end */ } +}; + +static struct driver_d anatop_regulator_driver = { + .name = "anatop_regulator", + .probe = anatop_regulator_probe, + .of_compatible = DRV_OF_COMPAT(of_anatop_regulator_match_tbl), +}; +device_platform_driver(anatop_regulator_driver); + diff --git a/drivers/regulator/bcm2835.c b/drivers/regulator/bcm2835.c index 0ada05db16..ea7cf7fe1e 100644 --- a/drivers/regulator/bcm2835.c +++ b/drivers/regulator/bcm2835.c @@ -24,6 +24,7 @@ static struct regulator_bcm2835 { struct device_d *dev; struct regulator_dev rdev; + struct regulator_desc rdesc; } regs[] = { REG_DEV(BCM2835_MBOX_POWER_DEVID_SDHCI, "bcm2835_mci0"), REG_DEV(BCM2835_MBOX_POWER_DEVID_UART0, "uart0-pl0110"), @@ -108,7 +109,7 @@ static int regulator_bcm2835_is_enabled(struct regulator_dev *rdev) return msg_pwr->get_power_state.body.resp.state; } -static struct regulator_ops bcm2835_ops = { +const static struct regulator_ops bcm2835_ops = { .enable = regulator_bcm2835_enable, .disable = regulator_bcm2835_disable, .is_enabled = regulator_bcm2835_is_enabled, @@ -122,7 +123,8 @@ static int regulator_bcm2835_probe(struct device_d *dev) for (i = 0; i < ARRAY_SIZE(regs); i++) { rb = ®s[i]; - rb->rdev.ops = &bcm2835_ops; + rb->rdesc.ops = &bcm2835_ops; + rb->rdev.desc = &rb->rdesc; rb->dev = dev; ret = dev_regulator_register(&rb->rdev, rb->devname, NULL); diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 39df980dcb..4ca035ae94 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -43,6 +43,15 @@ struct regulator { struct device_d *dev; }; +static int regulator_map_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV) +{ + if (rdev->desc->ops->list_voltage == regulator_list_voltage_linear) + return regulator_map_voltage_linear(rdev, min_uV, max_uV); + + return -ENOSYS; +} + static int regulator_enable_internal(struct regulator_internal *ri) { int ret; @@ -52,10 +61,10 @@ static int regulator_enable_internal(struct regulator_internal *ri) return 0; } - if (!ri->rdev->ops->enable) + if (!ri->rdev->desc->ops->enable) return -ENOSYS; - ret = ri->rdev->ops->enable(ri->rdev); + ret = ri->rdev->desc->ops->enable(ri->rdev); if (ret) return ret; @@ -74,10 +83,10 @@ static int regulator_disable_internal(struct regulator_internal *ri) if (!ri->enable_count) return -EINVAL; - if (!ri->rdev->ops->disable) + if (!ri->rdev->desc->ops->disable) return -ENOSYS; - ret = ri->rdev->ops->disable(ri->rdev); + ret = ri->rdev->desc->ops->disable(ri->rdev); if (ret) return ret; @@ -86,6 +95,33 @@ static int regulator_disable_internal(struct regulator_internal *ri) return 0; } +static int regulator_set_voltage_internal(struct regulator_internal *ri, + int min_uV, int max_uV) +{ + struct regulator_dev *rdev = ri->rdev; + const struct regulator_ops *ops = rdev->desc->ops; + unsigned int selector; + int best_val = 0; + int ret; + + if (ops->set_voltage_sel) { + ret = regulator_map_voltage(rdev, min_uV, max_uV); + if (ret >= 0) { + best_val = ops->list_voltage(rdev, ret); + if (min_uV <= best_val && max_uV >= best_val) { + selector = ret; + ret = ops->set_voltage_sel(rdev, selector); + } else { + ret = -EINVAL; + } + } + + return ret; + } + + return -ENOSYS; +} + static struct regulator_internal * __regulator_register(struct regulator_dev *rd, const char *name) { struct regulator_internal *ri; @@ -191,7 +227,12 @@ static struct regulator_internal *of_regulator_get(struct device_d *dev, const c } } - ri = ERR_PTR(-ENODEV); + /* + * It is possible that regulator we are looking for will be + * added in future initcalls, so, instead of reporting a + * complete failure report probe deferral + */ + ri = ERR_PTR(-EPROBE_DEFER); out: free(propname); @@ -320,6 +361,14 @@ int regulator_disable(struct regulator *r) return regulator_disable_internal(r->ri); } +int regulator_set_voltage(struct regulator *r, int min_uV, int max_uV) +{ + if (!r) + return 0; + + return regulator_set_voltage_internal(r->ri, min_uV, max_uV); +} + static void regulator_print_one(struct regulator_internal *ri) { struct regulator *r; diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 87554d22e3..cb5d785817 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -29,6 +29,7 @@ struct regulator_fixed { int active_low; int always_on; struct regulator_dev rdev; + struct regulator_desc rdesc; }; static int regulator_fixed_enable(struct regulator_dev *rdev) @@ -54,7 +55,7 @@ static int regulator_fixed_disable(struct regulator_dev *rdev) return gpio_direction_output(fix->gpio, fix->active_low); } -static struct regulator_ops fixed_ops = { +const static struct regulator_ops fixed_ops = { .enable = regulator_fixed_enable, .disable = regulator_fixed_disable, }; @@ -82,7 +83,8 @@ static int regulator_fixed_probe(struct device_d *dev) fix->active_low = 1; } - fix->rdev.ops = &fixed_ops; + fix->rdesc.ops = &fixed_ops; + fix->rdev.desc = &fix->rdesc; if (of_find_property(dev->device_node, "regulator-always-on", NULL)) { fix->always_on = 1; diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c new file mode 100644 index 0000000000..f22d21b35d --- /dev/null +++ b/drivers/regulator/helpers.c @@ -0,0 +1,186 @@ +#include <common.h> +#include <regmap.h> +#include <regulator.h> + +/** + * regulator_is_enabled_regmap - standard is_enabled() for regmap users + * + * @rdev: regulator to operate on + * + * Regulators that use regmap for their register I/O can set the + * enable_reg and enable_mask fields in their descriptor and then use + * this as their is_enabled operation, saving some code. + */ +int regulator_is_enabled_regmap(struct regulator_dev *rdev) +{ + unsigned int val; + int ret; + + ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val); + if (ret != 0) + return ret; + + val &= rdev->desc->enable_mask; + + if (rdev->desc->enable_is_inverted) { + if (rdev->desc->enable_val) + return val != rdev->desc->enable_val; + return val == 0; + } else { + if (rdev->desc->enable_val) + return val == rdev->desc->enable_val; + return val != 0; + } +} +EXPORT_SYMBOL_GPL(regulator_is_enabled_regmap); + +/** + * regulator_enable_regmap - standard enable() for regmap users + * + * @rdev: regulator to operate on + * + * Regulators that use regmap for their register I/O can set the + * enable_reg and enable_mask fields in their descriptor and then use + * this as their enable() operation, saving some code. + */ +int regulator_enable_regmap(struct regulator_dev *rdev) +{ + unsigned int val; + + if (rdev->desc->enable_is_inverted) { + val = rdev->desc->disable_val; + } else { + val = rdev->desc->enable_val; + if (!val) + val = rdev->desc->enable_mask; + } + + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, val); +} +EXPORT_SYMBOL_GPL(regulator_enable_regmap); + +/** + * regulator_disable_regmap - standard disable() for regmap users + * + * @rdev: regulator to operate on + * + * Regulators that use regmap for their register I/O can set the + * enable_reg and enable_mask fields in their descriptor and then use + * this as their disable() operation, saving some code. + */ +int regulator_disable_regmap(struct regulator_dev *rdev) +{ + unsigned int val; + + if (rdev->desc->enable_is_inverted) { + val = rdev->desc->enable_val; + if (!val) + val = rdev->desc->enable_mask; + } else { + val = rdev->desc->disable_val; + } + + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, val); +} +EXPORT_SYMBOL_GPL(regulator_disable_regmap); + +/** + * regulator_set_voltage_sel_regmap - standard set_voltage_sel for regmap users + * + * @rdev: regulator to operate on + * @sel: Selector to set + * + * Regulators that use regmap for their register I/O can set the + * vsel_reg and vsel_mask fields in their descriptor and then use this + * as their set_voltage_vsel operation, saving some code. + */ +int regulator_set_voltage_sel_regmap(struct regulator_dev *rdev, unsigned sel) +{ + int ret; + + sel <<= ffs(rdev->desc->vsel_mask) - 1; + + ret = regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg, + rdev->desc->vsel_mask, sel); + if (ret) + return ret; + + if (rdev->desc->apply_bit) + ret = regmap_update_bits(rdev->regmap, rdev->desc->apply_reg, + rdev->desc->apply_bit, + rdev->desc->apply_bit); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_set_voltage_sel_regmap); + +/** + * regulator_map_voltage_linear - map_voltage() for simple linear mappings + * + * @rdev: Regulator to operate on + * @min_uV: Lower bound for voltage + * @max_uV: Upper bound for voltage + * + * Drivers providing min_uV and uV_step in their regulator_desc can + * use this as their map_voltage() operation. + */ +int regulator_map_voltage_linear(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + int ret, voltage; + + /* Allow uV_step to be 0 for fixed voltage */ + if (rdev->desc->n_voltages == 1 && rdev->desc->uV_step == 0) { + if (min_uV <= rdev->desc->min_uV && rdev->desc->min_uV <= max_uV) + return 0; + else + return -EINVAL; + } + + if (!rdev->desc->uV_step) { + BUG_ON(!rdev->desc->uV_step); + return -EINVAL; + } + + if (min_uV < rdev->desc->min_uV) + min_uV = rdev->desc->min_uV; + + ret = DIV_ROUND_UP(min_uV - rdev->desc->min_uV, rdev->desc->uV_step); + if (ret < 0) + return ret; + + ret += rdev->desc->linear_min_sel; + + /* Map back into a voltage to verify we're still in bounds */ + voltage = rdev->desc->ops->list_voltage(rdev, ret); + if (voltage < min_uV || voltage > max_uV) + return -EINVAL; + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_map_voltage_linear); + +/** + * regulator_list_voltage_linear - List voltages with simple calculation + * + * @rdev: Regulator device + * @selector: Selector to convert into a voltage + * + * Regulators with a simple linear mapping between voltages and + * selectors can set min_uV and uV_step in the regulator descriptor + * and then use this function as their list_voltage() operation, + */ +int regulator_list_voltage_linear(struct regulator_dev *rdev, + unsigned int selector) +{ + if (selector >= rdev->desc->n_voltages) + return -EINVAL; + if (selector < rdev->desc->linear_min_sel) + return 0; + + selector -= rdev->desc->linear_min_sel; + + return rdev->desc->min_uV + (rdev->desc->uV_step * selector); +} +EXPORT_SYMBOL_GPL(regulator_list_voltage_linear); diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index c9d04f7978..caf1dc9acb 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -11,3 +11,14 @@ menuconfig RESET_CONTROLLER via GPIOs or SoC-internal reset controller modules. If unsure, say no. + +if RESET_CONTROLLER + +config RESET_IMX7 + bool "i.MX7 Reset Driver" + default SOC_IMX7D + select MFD_SYSCON + help + This enables the reset controller driver for i.MX7 SoCs. + +endif diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 52b10cd480..0b55caa204 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_RESET_CONTROLLER) += core.o obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o +obj-$(CONFIG_RESET_IMX7) += reset-imx7.o diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 59f75ca475..99b9c80655 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -147,8 +147,8 @@ EXPORT_SYMBOL_GPL(reset_control_deassert); * * Use of id names is optional. */ -struct reset_control *of_reset_control_get(struct device_node *node, - const char *id) +static struct reset_control *of_reset_control_get(struct device_node *node, + const char *id) { struct reset_control *rstc = ERR_PTR(-ENODEV); struct reset_controller_dev *r, *rcdev; @@ -194,9 +194,9 @@ struct reset_control *of_reset_control_get(struct device_node *node, return rstc; } -EXPORT_SYMBOL_GPL(of_reset_control_get); -struct reset_control *gpio_reset_control_get(struct device_d *dev, const char *id) +static struct reset_control * +gpio_reset_control_get(struct device_d *dev, const char *id) { struct reset_control *rc; int gpio; diff --git a/drivers/reset/reset-imx7.c b/drivers/reset/reset-imx7.c new file mode 100644 index 0000000000..9d4344a94c --- /dev/null +++ b/drivers/reset/reset-imx7.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2017, Impinj, Inc. + * + * i.MX7 System Reset Controller (SRC) driver + * + * Author: Andrey Smirnov <andrew.smirnov@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <common.h> +#include <dt-bindings/reset/imx7-reset.h> +#include <dt-bindings/reset/imx8mq-reset.h> +#include <init.h> +#include <linux/err.h> +#include <linux/reset-controller.h> +#include <mfd/syscon.h> +#include <regmap.h> +#include <of_device.h> + +struct imx7_src_signal { + unsigned int offset, bit; +}; + +struct imx7_src_variant { + const struct imx7_src_signal *signals; + unsigned int signals_num; + struct reset_control_ops ops; +}; + +struct imx7_src { + struct reset_controller_dev rcdev; + struct regmap *regmap; + const struct imx7_src_signal *signals; +}; + +enum imx7_src_registers { + SRC_A7RCR0 = 0x0004, + SRC_M4RCR = 0x000c, + SRC_ERCR = 0x0014, + SRC_HSICPHY_RCR = 0x001c, + SRC_USBOPHY1_RCR = 0x0020, + SRC_USBOPHY2_RCR = 0x0024, + SRC_MIPIPHY_RCR = 0x0028, + SRC_PCIEPHY_RCR = 0x002c, + SRC_DDRC_RCR = 0x1000, +}; + +static int imx7_reset_update(struct imx7_src *imx7src, + unsigned long id, unsigned int value) +{ + const struct imx7_src_signal *signal = &imx7src->signals[id]; + + return regmap_update_bits(imx7src->regmap, + signal->offset, signal->bit, value); +} + +static const struct imx7_src_signal imx7_src_signals[IMX7_RESET_NUM] = { + [IMX7_RESET_A7_CORE_POR_RESET0] = { SRC_A7RCR0, BIT(0) }, + [IMX7_RESET_A7_CORE_POR_RESET1] = { SRC_A7RCR0, BIT(1) }, + [IMX7_RESET_A7_CORE_RESET0] = { SRC_A7RCR0, BIT(4) }, + [IMX7_RESET_A7_CORE_RESET1] = { SRC_A7RCR0, BIT(5) }, + [IMX7_RESET_A7_DBG_RESET0] = { SRC_A7RCR0, BIT(8) }, + [IMX7_RESET_A7_DBG_RESET1] = { SRC_A7RCR0, BIT(9) }, + [IMX7_RESET_A7_ETM_RESET0] = { SRC_A7RCR0, BIT(12) }, + [IMX7_RESET_A7_ETM_RESET1] = { SRC_A7RCR0, BIT(13) }, + [IMX7_RESET_A7_SOC_DBG_RESET] = { SRC_A7RCR0, BIT(20) }, + [IMX7_RESET_A7_L2RESET] = { SRC_A7RCR0, BIT(21) }, + [IMX7_RESET_SW_M4C_RST] = { SRC_M4RCR, BIT(1) }, + [IMX7_RESET_SW_M4P_RST] = { SRC_M4RCR, BIT(2) }, + [IMX7_RESET_EIM_RST] = { SRC_ERCR, BIT(0) }, + [IMX7_RESET_HSICPHY_PORT_RST] = { SRC_HSICPHY_RCR, BIT(1) }, + [IMX7_RESET_USBPHY1_POR] = { SRC_USBOPHY1_RCR, BIT(0) }, + [IMX7_RESET_USBPHY1_PORT_RST] = { SRC_USBOPHY1_RCR, BIT(1) }, + [IMX7_RESET_USBPHY2_POR] = { SRC_USBOPHY2_RCR, BIT(0) }, + [IMX7_RESET_USBPHY2_PORT_RST] = { SRC_USBOPHY2_RCR, BIT(1) }, + [IMX7_RESET_MIPI_PHY_MRST] = { SRC_MIPIPHY_RCR, BIT(1) }, + [IMX7_RESET_MIPI_PHY_SRST] = { SRC_MIPIPHY_RCR, BIT(2) }, + [IMX7_RESET_PCIEPHY] = { SRC_PCIEPHY_RCR, BIT(2) | BIT(1) }, + [IMX7_RESET_PCIEPHY_PERST] = { SRC_PCIEPHY_RCR, BIT(3) }, + [IMX7_RESET_PCIE_CTRL_APPS_EN] = { SRC_PCIEPHY_RCR, BIT(6) }, + [IMX7_RESET_DDRC_PRST] = { SRC_DDRC_RCR, BIT(0) }, + [IMX7_RESET_DDRC_CORE_RST] = { SRC_DDRC_RCR, BIT(1) }, +}; + +static struct imx7_src *to_imx7_src(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct imx7_src, rcdev); +} + +static int imx7_reset_set(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + struct imx7_src *imx7src = to_imx7_src(rcdev); + const unsigned int bit = imx7src->signals[id].bit; + unsigned int value = assert ? bit : 0; + + switch (id) { + case IMX7_RESET_PCIEPHY: + /* + * wait for more than 10us to release phy g_rst and + * btnrst + */ + if (!assert) + udelay(10); + break; + + case IMX7_RESET_PCIE_CTRL_APPS_EN: + value = assert ? 0 : bit; + break; + } + + return imx7_reset_update(imx7src, id, value); +} + +static int imx7_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return imx7_reset_set(rcdev, id, true); +} + +static int imx7_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return imx7_reset_set(rcdev, id, false); +} + +static const struct imx7_src_variant variant_imx7 = { + .signals = imx7_src_signals, + .signals_num = ARRAY_SIZE(imx7_src_signals), + .ops = { + .assert = imx7_reset_assert, + .deassert = imx7_reset_deassert, + }, +}; + +enum imx8mq_src_registers { + SRC_A53RCR0 = 0x0004, + SRC_HDMI_RCR = 0x0030, + SRC_DISP_RCR = 0x0034, + SRC_GPU_RCR = 0x0040, + SRC_VPU_RCR = 0x0044, + SRC_PCIE2_RCR = 0x0048, + SRC_MIPIPHY1_RCR = 0x004c, + SRC_MIPIPHY2_RCR = 0x0050, + SRC_DDRC2_RCR = 0x1004, +}; + +static const struct imx7_src_signal imx8mq_src_signals[IMX8MQ_RESET_NUM] = { + [IMX8MQ_RESET_A53_CORE_POR_RESET0] = { SRC_A53RCR0, BIT(0) }, + [IMX8MQ_RESET_A53_CORE_POR_RESET1] = { SRC_A53RCR0, BIT(1) }, + [IMX8MQ_RESET_A53_CORE_POR_RESET2] = { SRC_A53RCR0, BIT(2) }, + [IMX8MQ_RESET_A53_CORE_POR_RESET3] = { SRC_A53RCR0, BIT(3) }, + [IMX8MQ_RESET_A53_CORE_RESET0] = { SRC_A53RCR0, BIT(4) }, + [IMX8MQ_RESET_A53_CORE_RESET1] = { SRC_A53RCR0, BIT(5) }, + [IMX8MQ_RESET_A53_CORE_RESET2] = { SRC_A53RCR0, BIT(6) }, + [IMX8MQ_RESET_A53_CORE_RESET3] = { SRC_A53RCR0, BIT(7) }, + [IMX8MQ_RESET_A53_DBG_RESET0] = { SRC_A53RCR0, BIT(8) }, + [IMX8MQ_RESET_A53_DBG_RESET1] = { SRC_A53RCR0, BIT(9) }, + [IMX8MQ_RESET_A53_DBG_RESET2] = { SRC_A53RCR0, BIT(10) }, + [IMX8MQ_RESET_A53_DBG_RESET3] = { SRC_A53RCR0, BIT(11) }, + [IMX8MQ_RESET_A53_ETM_RESET0] = { SRC_A53RCR0, BIT(12) }, + [IMX8MQ_RESET_A53_ETM_RESET1] = { SRC_A53RCR0, BIT(13) }, + [IMX8MQ_RESET_A53_ETM_RESET2] = { SRC_A53RCR0, BIT(14) }, + [IMX8MQ_RESET_A53_ETM_RESET3] = { SRC_A53RCR0, BIT(15) }, + [IMX8MQ_RESET_A53_SOC_DBG_RESET] = { SRC_A53RCR0, BIT(20) }, + [IMX8MQ_RESET_A53_L2RESET] = { SRC_A53RCR0, BIT(21) }, + [IMX8MQ_RESET_SW_NON_SCLR_M4C_RST] = { SRC_M4RCR, BIT(0) }, + [IMX8MQ_RESET_OTG1_PHY_RESET] = { SRC_USBOPHY1_RCR, BIT(0) }, + [IMX8MQ_RESET_OTG2_PHY_RESET] = { SRC_USBOPHY2_RCR, BIT(0) }, + [IMX8MQ_RESET_MIPI_DSI_RESET_BYTE_N] = { SRC_MIPIPHY_RCR, BIT(1) }, + [IMX8MQ_RESET_MIPI_DSI_RESET_N] = { SRC_MIPIPHY_RCR, BIT(2) }, + [IMX8MQ_RESET_MIPI_DIS_DPI_RESET_N] = { SRC_MIPIPHY_RCR, BIT(3) }, + [IMX8MQ_RESET_MIPI_DIS_ESC_RESET_N] = { SRC_MIPIPHY_RCR, BIT(4) }, + [IMX8MQ_RESET_MIPI_DIS_PCLK_RESET_N] = { SRC_MIPIPHY_RCR, BIT(5) }, + [IMX8MQ_RESET_PCIEPHY] = { SRC_PCIEPHY_RCR, + BIT(2) | BIT(1) }, + [IMX8MQ_RESET_PCIEPHY_PERST] = { SRC_PCIEPHY_RCR, BIT(3) }, + [IMX8MQ_RESET_PCIE_CTRL_APPS_EN] = { SRC_PCIEPHY_RCR, BIT(6) }, + [IMX8MQ_RESET_PCIE_CTRL_APPS_TURNOFF] = { SRC_PCIEPHY_RCR, BIT(11) }, + [IMX8MQ_RESET_HDMI_PHY_APB_RESET] = { SRC_HDMI_RCR, BIT(0) }, + [IMX8MQ_RESET_DISP_RESET] = { SRC_DISP_RCR, BIT(0) }, + [IMX8MQ_RESET_GPU_RESET] = { SRC_GPU_RCR, BIT(0) }, + [IMX8MQ_RESET_VPU_RESET] = { SRC_VPU_RCR, BIT(0) }, + [IMX8MQ_RESET_PCIEPHY2] = { SRC_PCIE2_RCR, + BIT(2) | BIT(1) }, + [IMX8MQ_RESET_PCIEPHY2_PERST] = { SRC_PCIE2_RCR, BIT(3) }, + [IMX8MQ_RESET_PCIE2_CTRL_APPS_EN] = { SRC_PCIE2_RCR, BIT(6) }, + [IMX8MQ_RESET_PCIE2_CTRL_APPS_TURNOFF] = { SRC_PCIE2_RCR, BIT(11) }, + [IMX8MQ_RESET_MIPI_CSI1_CORE_RESET] = { SRC_MIPIPHY1_RCR, BIT(0) }, + [IMX8MQ_RESET_MIPI_CSI1_PHY_REF_RESET] = { SRC_MIPIPHY1_RCR, BIT(1) }, + [IMX8MQ_RESET_MIPI_CSI1_ESC_RESET] = { SRC_MIPIPHY1_RCR, BIT(2) }, + [IMX8MQ_RESET_MIPI_CSI2_CORE_RESET] = { SRC_MIPIPHY2_RCR, BIT(0) }, + [IMX8MQ_RESET_MIPI_CSI2_PHY_REF_RESET] = { SRC_MIPIPHY2_RCR, BIT(1) }, + [IMX8MQ_RESET_MIPI_CSI2_ESC_RESET] = { SRC_MIPIPHY2_RCR, BIT(2) }, + [IMX8MQ_RESET_DDRC1_PRST] = { SRC_DDRC_RCR, BIT(0) }, + [IMX8MQ_RESET_DDRC1_CORE_RESET] = { SRC_DDRC_RCR, BIT(1) }, + [IMX8MQ_RESET_DDRC1_PHY_RESET] = { SRC_DDRC_RCR, BIT(2) }, + [IMX8MQ_RESET_DDRC2_PHY_RESET] = { SRC_DDRC2_RCR, BIT(0) }, + [IMX8MQ_RESET_DDRC2_CORE_RESET] = { SRC_DDRC2_RCR, BIT(1) }, + [IMX8MQ_RESET_DDRC2_PRST] = { SRC_DDRC2_RCR, BIT(2) }, +}; + +static int imx8mq_reset_set(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + struct imx7_src *imx7src = to_imx7_src(rcdev); + const unsigned int bit = imx7src->signals[id].bit; + unsigned int value = assert ? bit : 0; + + switch (id) { + case IMX8MQ_RESET_PCIEPHY: + case IMX8MQ_RESET_PCIEPHY2: /* fallthrough */ + /* + * wait for more than 10us to release phy g_rst and + * btnrst + */ + if (!assert) + udelay(10); + break; + + case IMX8MQ_RESET_PCIE_CTRL_APPS_EN: + case IMX8MQ_RESET_PCIE2_CTRL_APPS_EN: /* fallthrough */ + case IMX8MQ_RESET_MIPI_DIS_PCLK_RESET_N: /* fallthrough */ + case IMX8MQ_RESET_MIPI_DIS_ESC_RESET_N: /* fallthrough */ + case IMX8MQ_RESET_MIPI_DIS_DPI_RESET_N: /* fallthrough */ + case IMX8MQ_RESET_MIPI_DSI_RESET_N: /* fallthrough */ + case IMX8MQ_RESET_MIPI_DSI_RESET_BYTE_N: /* fallthrough */ + value = assert ? 0 : bit; + break; + } + + return imx7_reset_update(imx7src, id, value); +} + +static int imx8mq_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return imx8mq_reset_set(rcdev, id, true); +} + +static int imx8mq_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return imx8mq_reset_set(rcdev, id, false); +} + +static const struct imx7_src_variant variant_imx8mq = { + .signals = imx8mq_src_signals, + .signals_num = ARRAY_SIZE(imx8mq_src_signals), + .ops = { + .assert = imx8mq_reset_assert, + .deassert = imx8mq_reset_deassert, + }, +}; + +static int imx7_reset_probe(struct device_d *dev) +{ + struct imx7_src *imx7src; + const struct imx7_src_variant *variant = of_device_get_match_data(dev); + + imx7src = xzalloc(sizeof(*imx7src)); + imx7src->signals = variant->signals; + imx7src->regmap = syscon_node_to_regmap(dev->device_node); + if (IS_ERR(imx7src->regmap)) { + dev_err(dev, "Unable to get imx7-src regmap"); + return PTR_ERR(imx7src->regmap); + } + + imx7src->rcdev.nr_resets = variant->signals_num; + imx7src->rcdev.ops = &variant->ops; + imx7src->rcdev.of_node = dev->device_node; + + return reset_controller_register(&imx7src->rcdev); +} + +static const struct of_device_id imx7_reset_dt_ids[] = { + { .compatible = "fsl,imx7d-src", .data = &variant_imx7 }, + { .compatible = "fsl,imx8mq-src", .data = &variant_imx8mq }, + { /* sentinel */ }, +}; + +static struct driver_d imx7_reset_driver = { + .name = "imx7d-src", + .probe = imx7_reset_probe, + .of_compatible = DRV_OF_COMPAT(imx7_reset_dt_ids), +}; +device_platform_driver(imx7_reset_driver); diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c index b6faa0217e..9b499f23c5 100644 --- a/drivers/reset/reset-socfpga.c +++ b/drivers/reset/reset-socfpga.c @@ -73,7 +73,7 @@ static int socfpga_reset_deassert(struct reset_controller_dev *rcdev, return 0; } -static struct reset_control_ops socfpga_reset_ops = { +static const struct reset_control_ops socfpga_reset_ops = { .assert = socfpga_reset_assert, .deassert = socfpga_reset_deassert, }; diff --git a/drivers/soc/imx/Kconfig b/drivers/soc/imx/Kconfig new file mode 100644 index 0000000000..32ec76feaf --- /dev/null +++ b/drivers/soc/imx/Kconfig @@ -0,0 +1,9 @@ +menu "i.MX SoC drivers" + +config IMX_GPCV2_PM_DOMAINS + bool "i.MX GPCv2 PM domains" + depends on ARCH_IMX7 || ARCH_IMX8MQ + select PM_GENERIC_DOMAINS + default y if ARCH_IMX7 || ARCH_IMX8MQ + +endmenu diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile new file mode 100644 index 0000000000..d60056c7b6 --- /dev/null +++ b/drivers/soc/imx/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c new file mode 100644 index 0000000000..bc373ecf40 --- /dev/null +++ b/drivers/soc/imx/gpcv2.c @@ -0,0 +1,498 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2017 Impinj, Inc + * Author: Andrey Smirnov <andrew.smirnov@gmail.com> + * + * Based on the code of analogus driver: + * + * Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de> + */ + +#include <of_device.h> +#include <common.h> +#include <clock.h> +#include <abort.h> +#include <malloc.h> +#include <io.h> +#include <init.h> +#include <linux/iopoll.h> + +#include <pm_domain.h> +#include <regulator.h> +#include <dt-bindings/power/imx7-power.h> + +#include <dt-bindings/power/imx8mq-power.h> + +#define GPC_LPCR_A_BSC 0x000 + +#define GPC_PGC_CPU_MAPPING 0x0ec + +#define IMX7_USB_HSIC_PHY_A_DOMAIN BIT(6) +#define IMX7_USB_OTG2_PHY_A_DOMAIN BIT(5) +#define IMX7_USB_OTG1_PHY_A_DOMAIN BIT(4) +#define IMX7_PCIE_PHY_A_DOMAIN BIT(3) +#define IMX7_MIPI_PHY_A_DOMAIN BIT(2) + +#define IMX8M_PCIE2_A53_DOMAIN BIT(15) +#define IMX8M_MIPI_CSI2_A53_DOMAIN BIT(14) +#define IMX8M_MIPI_CSI1_A53_DOMAIN BIT(13) +#define IMX8M_DISP_A53_DOMAIN BIT(12) +#define IMX8M_HDMI_A53_DOMAIN BIT(11) +#define IMX8M_VPU_A53_DOMAIN BIT(10) +#define IMX8M_GPU_A53_DOMAIN BIT(9) +#define IMX8M_DDR2_A53_DOMAIN BIT(8) +#define IMX8M_DDR1_A53_DOMAIN BIT(7) +#define IMX8M_OTG2_A53_DOMAIN BIT(5) +#define IMX8M_OTG1_A53_DOMAIN BIT(4) +#define IMX8M_PCIE1_A53_DOMAIN BIT(3) +#define IMX8M_MIPI_A53_DOMAIN BIT(2) + +#define GPC_PU_PGC_SW_PUP_REQ 0x0f8 +#define GPC_PU_PGC_SW_PDN_REQ 0x104 + +#define IMX7_USB_HSIC_PHY_SW_Pxx_REQ BIT(4) +#define IMX7_USB_OTG2_PHY_SW_Pxx_REQ BIT(3) +#define IMX7_USB_OTG1_PHY_SW_Pxx_REQ BIT(2) +#define IMX7_PCIE_PHY_SW_Pxx_REQ BIT(1) +#define IMX7_MIPI_PHY_SW_Pxx_REQ BIT(0) + +#define IMX8M_PCIE2_SW_Pxx_REQ BIT(13) +#define IMX8M_MIPI_CSI2_SW_Pxx_REQ BIT(12) +#define IMX8M_MIPI_CSI1_SW_Pxx_REQ BIT(11) +#define IMX8M_DISP_SW_Pxx_REQ BIT(10) +#define IMX8M_HDMI_SW_Pxx_REQ BIT(9) +#define IMX8M_VPU_SW_Pxx_REQ BIT(8) +#define IMX8M_GPU_SW_Pxx_REQ BIT(7) +#define IMX8M_DDR2_SW_Pxx_REQ BIT(6) +#define IMX8M_DDR1_SW_Pxx_REQ BIT(5) +#define IMX8M_OTG2_SW_Pxx_REQ BIT(3) +#define IMX8M_OTG1_SW_Pxx_REQ BIT(2) +#define IMX8M_PCIE1_SW_Pxx_REQ BIT(1) +#define IMX8M_MIPI_SW_Pxx_REQ BIT(0) + +#define GPC_M4_PU_PDN_FLG 0x1bc + +/* + * The PGC offset values in Reference Manual + * (Rev. 1, 01/2018 and the older ones) GPC chapter's + * GPC_PGC memory map are incorrect, below offset + * values are from design RTL. + */ +#define IMX7_PGC_MIPI 16 +#define IMX7_PGC_PCIE 17 +#define IMX7_PGC_USB_HSIC 20 + + +#define IMX8M_PGC_MIPI 16 +#define IMX8M_PGC_PCIE1 17 +#define IMX8M_PGC_OTG1 18 +#define IMX8M_PGC_OTG2 19 +#define IMX8M_PGC_DDR1 21 +#define IMX8M_PGC_GPU 23 +#define IMX8M_PGC_VPU 24 +#define IMX8M_PGC_DISP 26 +#define IMX8M_PGC_MIPI_CSI1 27 +#define IMX8M_PGC_MIPI_CSI2 28 +#define IMX8M_PGC_PCIE2 29 + +#define GPC_PGC_CTRL(n) (0x800 + (n) * 0x40) +#define GPC_PGC_SR(n) (GPC_PGC_CTRL(n) + 0xc) + +#define GPC_PGC_CTRL_PCR BIT(0) + +struct imx_pgc_domain { + struct generic_pm_domain genpd; + void __iomem *base; + struct regulator *regulator; + + unsigned int pgc; + + const struct { + u32 pxx; + u32 map; + } bits; + + const int voltage; + struct device_d *dev; +}; + +struct imx_pgc_domain_data { + const struct imx_pgc_domain *domains; + size_t domains_num; +}; + +static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd, + bool on) +{ + struct imx_pgc_domain *domain = container_of(genpd, + struct imx_pgc_domain, + genpd); + unsigned int offset = on ? + GPC_PU_PGC_SW_PUP_REQ : GPC_PU_PGC_SW_PDN_REQ; + const bool enable_power_control = !on; + const bool has_regulator = !IS_ERR(domain->regulator); + int ret = 0; + unsigned int mapping, ctrl = 0, pxx; + + mapping = readl(domain->base + GPC_PGC_CPU_MAPPING); + mapping |= domain->bits.map; + writel(mapping, domain->base + GPC_PGC_CPU_MAPPING); + + if (has_regulator && on) { + ret = regulator_enable(domain->regulator); + if (ret) { + dev_err(domain->dev, "failed to enable regulator\n"); + goto unmap; + } + } + + if (enable_power_control) { + ctrl = readl(domain->base + GPC_PGC_CTRL(domain->pgc)); + ctrl |= GPC_PGC_CTRL_PCR; + writel(ctrl, domain->base + GPC_PGC_CTRL(domain->pgc)); + } + + pxx = readl(domain->base + offset); + pxx |= domain->bits.pxx; + writel(pxx, domain->base + offset); + + /* + * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait + * for PUP_REQ/PDN_REQ bit to be cleared + */ + ret = readl_poll_timeout(domain->base + offset, pxx, + !(pxx & domain->bits.pxx), MSECOND); + if (ret < 0) { + dev_err(domain->dev, "falied to command PGC\n"); + /* + * If we were in a process of enabling a + * domain and failed we might as well disable + * the regulator we just enabled. And if it + * was the opposite situation and we failed to + * power down -- keep the regulator on + */ + on = !on; + } + + if (enable_power_control) { + ctrl &= ~GPC_PGC_CTRL_PCR; + writel(ctrl, domain->base + GPC_PGC_CTRL(domain->pgc)); + } + + if (has_regulator && !on) { + int err; + + err = regulator_disable(domain->regulator); + if (err) + dev_err(domain->dev, + "failed to disable regulator: %d\n", ret); + /* Preserve earlier error code */ + ret = ret ?: err; + } +unmap: + mapping &= ~domain->bits.map; + writel(mapping, domain->base + GPC_PGC_CPU_MAPPING); + + return ret; +} + +static int imx_gpc_pu_pgc_sw_pup_req(struct generic_pm_domain *genpd) +{ + return imx_gpc_pu_pgc_sw_pxx_req(genpd, true); +} + +static int imx_gpc_pu_pgc_sw_pdn_req(struct generic_pm_domain *genpd) +{ + return imx_gpc_pu_pgc_sw_pxx_req(genpd, false); +} + +static const struct imx_pgc_domain imx7_pgc_domains[] = { + [IMX7_POWER_DOMAIN_MIPI_PHY] = { + .genpd = { + .name = "mipi-phy", + }, + .bits = { + .pxx = IMX7_MIPI_PHY_SW_Pxx_REQ, + .map = IMX7_MIPI_PHY_A_DOMAIN, + }, + .voltage = 1000000, + .pgc = IMX7_PGC_MIPI, + }, + + [IMX7_POWER_DOMAIN_PCIE_PHY] = { + .genpd = { + .name = "pcie-phy", + }, + .bits = { + .pxx = IMX7_PCIE_PHY_SW_Pxx_REQ, + .map = IMX7_PCIE_PHY_A_DOMAIN, + }, + .voltage = 1000000, + .pgc = IMX7_PGC_PCIE, + }, + + [IMX7_POWER_DOMAIN_USB_HSIC_PHY] = { + .genpd = { + .name = "usb-hsic-phy", + }, + .bits = { + .pxx = IMX7_USB_HSIC_PHY_SW_Pxx_REQ, + .map = IMX7_USB_HSIC_PHY_A_DOMAIN, + }, + .voltage = 1200000, + .pgc = IMX7_PGC_USB_HSIC, + }, +}; + +static const struct imx_pgc_domain_data imx7_pgc_domain_data = { + .domains = imx7_pgc_domains, + .domains_num = ARRAY_SIZE(imx7_pgc_domains), +}; + +static const struct imx_pgc_domain imx8m_pgc_domains[] = { + [IMX8M_POWER_DOMAIN_MIPI] = { + .genpd = { + .name = "mipi", + }, + .bits = { + .pxx = IMX8M_MIPI_SW_Pxx_REQ, + .map = IMX8M_MIPI_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_MIPI, + }, + + [IMX8M_POWER_DOMAIN_PCIE1] = { + .genpd = { + .name = "pcie1", + }, + .bits = { + .pxx = IMX8M_PCIE1_SW_Pxx_REQ, + .map = IMX8M_PCIE1_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_PCIE1, + }, + + [IMX8M_POWER_DOMAIN_USB_OTG1] = { + .genpd = { + .name = "usb-otg1", + }, + .bits = { + .pxx = IMX8M_OTG1_SW_Pxx_REQ, + .map = IMX8M_OTG1_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_OTG1, + }, + + [IMX8M_POWER_DOMAIN_USB_OTG2] = { + .genpd = { + .name = "usb-otg2", + }, + .bits = { + .pxx = IMX8M_OTG2_SW_Pxx_REQ, + .map = IMX8M_OTG2_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_OTG2, + }, + + [IMX8M_POWER_DOMAIN_DDR1] = { + .genpd = { + .name = "ddr1", + }, + .bits = { + .pxx = IMX8M_DDR1_SW_Pxx_REQ, + .map = IMX8M_DDR2_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_DDR1, + }, + + [IMX8M_POWER_DOMAIN_GPU] = { + .genpd = { + .name = "gpu", + }, + .bits = { + .pxx = IMX8M_GPU_SW_Pxx_REQ, + .map = IMX8M_GPU_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_GPU, + }, + + [IMX8M_POWER_DOMAIN_VPU] = { + .genpd = { + .name = "vpu", + }, + .bits = { + .pxx = IMX8M_VPU_SW_Pxx_REQ, + .map = IMX8M_VPU_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_VPU, + }, + + [IMX8M_POWER_DOMAIN_DISP] = { + .genpd = { + .name = "disp", + }, + .bits = { + .pxx = IMX8M_DISP_SW_Pxx_REQ, + .map = IMX8M_DISP_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_DISP, + }, + + [IMX8M_POWER_DOMAIN_MIPI_CSI1] = { + .genpd = { + .name = "mipi-csi1", + }, + .bits = { + .pxx = IMX8M_MIPI_CSI1_SW_Pxx_REQ, + .map = IMX8M_MIPI_CSI1_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_MIPI_CSI1, + }, + + [IMX8M_POWER_DOMAIN_MIPI_CSI2] = { + .genpd = { + .name = "mipi-csi2", + }, + .bits = { + .pxx = IMX8M_MIPI_CSI2_SW_Pxx_REQ, + .map = IMX8M_MIPI_CSI2_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_MIPI_CSI2, + }, + + [IMX8M_POWER_DOMAIN_PCIE2] = { + .genpd = { + .name = "pcie2", + }, + .bits = { + .pxx = IMX8M_PCIE2_SW_Pxx_REQ, + .map = IMX8M_PCIE2_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_PCIE2, + }, +}; + +static const struct imx_pgc_domain_data imx8m_pgc_domain_data = { + .domains = imx8m_pgc_domains, + .domains_num = ARRAY_SIZE(imx8m_pgc_domains), +}; + +static int imx_pgc_domain_probe(struct device_d *dev) +{ + struct imx_pgc_domain *domain = dev->priv; + int ret; + + domain->dev = dev; + + domain->regulator = regulator_get(domain->dev, "power"); + if (IS_ERR(domain->regulator)) { + if (PTR_ERR(domain->regulator) != -ENODEV) { + if (PTR_ERR(domain->regulator) != -EPROBE_DEFER) + dev_err(domain->dev, "Failed to get domain's regulator\n"); + return PTR_ERR(domain->regulator); + } + } else { + regulator_set_voltage(domain->regulator, + domain->voltage, domain->voltage); + } + + ret = pm_genpd_init(&domain->genpd, NULL, true); + if (ret) { + dev_err(domain->dev, "Failed to init power domain\n"); + return ret; + } + + ret = of_genpd_add_provider_simple(domain->dev->device_node, + &domain->genpd); + if (ret) { + dev_err(domain->dev, "Failed to add genpd provider\n"); + } + + return ret; +} + +static const struct platform_device_id imx_pgc_domain_id[] = { + { "imx-pgc-domain", }, + { }, +}; + +static struct driver_d imx_pgc_domain_driver = { + .name = "imx-pgc", + .probe = imx_pgc_domain_probe, + .id_table = imx_pgc_domain_id, +}; +coredevice_platform_driver(imx_pgc_domain_driver); + +static int imx_gpcv2_probe(struct device_d *dev) +{ + static const struct imx_pgc_domain_data *domain_data; + struct device_node *pgc_np, *np; + struct resource *res; + void __iomem *base; + int ret; + + pgc_np = of_get_child_by_name(dev->device_node, "pgc"); + if (!pgc_np) { + dev_err(dev, "No power domains specified in DT\n"); + return -EINVAL; + } + + res = dev_request_mem_resource(dev, 0); + if (IS_ERR(res)) + return PTR_ERR(res); + + base = IOMEM(res->start); + + domain_data = of_device_get_match_data(dev); + + for_each_child_of_node(pgc_np, np) { + struct device_d *pd_dev; + struct imx_pgc_domain *domain; + u32 domain_index; + ret = of_property_read_u32(np, "reg", &domain_index); + if (ret) { + dev_err(dev, "Failed to read 'reg' property\n"); + return ret; + } + + if (domain_index >= domain_data->domains_num) { + dev_warn(dev, + "Domain index %d is out of bounds\n", + domain_index); + continue; + } + + domain = xmemdup(&domain_data->domains[domain_index], + sizeof(domain_data->domains[domain_index])); + domain->base = base; + domain->genpd.power_on = imx_gpc_pu_pgc_sw_pup_req; + domain->genpd.power_off = imx_gpc_pu_pgc_sw_pdn_req; + + pd_dev = xzalloc(sizeof(*pd_dev)); + pd_dev->device_node = np; + pd_dev->id = domain_index; + pd_dev->parent = dev; + pd_dev->priv = domain; + pd_dev->device_node = np; + dev_set_name(pd_dev, imx_pgc_domain_id[0].name); + + ret = platform_device_register(pd_dev); + if (ret) + return ret; + } + + return 0; +} + +static const struct of_device_id imx_gpcv2_dt_ids[] = { + { .compatible = "fsl,imx7d-gpc", .data = &imx7_pgc_domain_data }, + { .compatible = "fsl,imx8mq-gpc", .data = &imx8m_pgc_domain_data, }, + { } +}; + +static struct driver_d imx_gpcv2_driver = { + .name = "imx7d-gpc", + .probe = imx_gpcv2_probe, + .of_compatible = DRV_OF_COMPAT(imx_gpcv2_dt_ids), +}; +coredevice_platform_driver(imx_gpcv2_driver); |