diff options
Diffstat (limited to 'drivers')
26 files changed, 2066 insertions, 210 deletions
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 78eabff2fe46..ac87bbc4f67a 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -97,6 +97,28 @@ config SATA_AHCI_PLATFORM If unsure, say N. +config SATA_PHY + bool "SATA PHY Framework" + default n + help + This option enables the SATA PHY utility framework APIs. + The framework acts as an interface between the SATA device + and the PHY device. The SATA PHY device registers itself + with the framework through the APIs provided and the SATA + device finds and requests for an appropriate PHY device. + +config SATA_EXYNOS + bool "Exynos SATA AHCI support" + select I2C + select HAVE_S3C2410_I2C + select I2C_S3C2410 + select SATA_PHY + help + This option enables support for Exynos AHCI Serial ATA + controllers. + + If unsure, say N. + config SATA_FSL tristate "Freescale 3.0Gbps SATA support" depends on FSL_SOC diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index c04d0fd038a3..32d49edfade3 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -10,6 +10,8 @@ obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o obj-$(CONFIG_SATA_SIL24) += sata_sil24.o obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o +obj-$(CONFIG_SATA_PHY) += sata_phy.o +obj-$(CONFIG_SATA_EXYNOS) += sata_exynos.o sata_exynos_phy.o libahci.o # SFF w/ custom DMA obj-$(CONFIG_PDC_ADMA) += pdc_adma.o diff --git a/drivers/ata/sata_exynos.c b/drivers/ata/sata_exynos.c new file mode 100644 index 000000000000..58c4e022ffaa --- /dev/null +++ b/drivers/ata/sata_exynos.c @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS - SATA controller driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/kernel.h> +#include <linux/gfp.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/libata.h> +#include <linux/ahci_platform.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/of.h> + +#include "ahci.h" +#include "sata_phy.h" + +#define MHZ (1000 * 1000) + +static const struct ata_port_info ahci_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_ops, +}; + +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT("ahci_platform"), +}; + +struct exynos_sata { + struct clk *sclk; + struct clk *clk; + struct sata_phy *phy; + int irq; + unsigned int freq; +}; + +static void exynos_sata_parse_dt(struct device_node *np, + struct exynos_sata *sata) +{ + if (!np) + return; + + of_property_read_u32(np, "samsung,sata-freq", &sata->freq); +} + +static int __init exynos_sata_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ata_port_info pi = ahci_port_info; + const struct ata_port_info *ppi[] = { &pi, NULL }; + struct ahci_host_priv *hpriv; + struct exynos_sata *sata; + struct ata_host *host; + struct resource *mem; + int n_ports, i, ret; + + sata = devm_kzalloc(dev, sizeof(*sata), GFP_KERNEL); + if (!sata) { + dev_err(dev, "can't alloc sata\n"); + return -EINVAL; + } + + hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); + if (!hpriv) { + dev_err(dev, "can't alloc ahci_host_priv\n"); + ret = -ENOMEM; + goto err1; + } + + hpriv->flags |= (unsigned long)pi.private_data; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(dev, "no mmio space\n"); + ret = -EINVAL; + goto err2; + } + + sata->irq = platform_get_irq(pdev, 0); + if (sata->irq <= 0) { + dev_err(dev, "no irq\n"); + ret = -EINVAL; + goto err2; + } + + hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem)); + if (!hpriv->mmio) { + dev_err(dev, "can't map %pR\n", mem); + ret = -ENOMEM; + goto err2; + } + + exynos_sata_parse_dt(dev->of_node, sata); + if (!sata->freq) { + dev_err(dev, "can't determine sata frequency \n"); + ret = -ENOMEM; + goto err2; + } + + sata->sclk = devm_clk_get(dev, "sclk_sata"); + if (IS_ERR(sata->sclk)) { + dev_err(dev, "failed to get sclk_sata\n"); + ret = PTR_ERR(sata->sclk); + goto err3; + } + clk_prepare_enable(sata->sclk); + + clk_set_rate(sata->sclk, sata->freq * MHZ); + + sata->clk = devm_clk_get(dev, "sata"); + if (IS_ERR(sata->clk)) { + dev_err(dev, "failed to get sata clock\n"); + ret = PTR_ERR(sata->clk); + goto err4; + } + clk_prepare_enable(sata->clk); + + /* Get a gen 3 PHY controller */ + + sata->phy = sata_get_phy(SATA_PHY_GENERATION3); + if (!sata->phy) { + dev_err(dev, "failed to get sata phy\n"); + ret = -EPROBE_DEFER; + goto err5; + } + + /* Initialize the controller */ + + ret = sata_init_phy(sata->phy); + if (ret < 0) { + dev_err(dev, "failed to initialize sata phy\n"); + goto err6; + } + + ahci_save_initial_config(dev, hpriv, 0, 0); + + /* prepare host */ + if (hpriv->cap & HOST_CAP_NCQ) + pi.flags |= ATA_FLAG_NCQ; + + if (hpriv->cap & HOST_CAP_PMP) + pi.flags |= ATA_FLAG_PMP; + + ahci_set_em_messages(hpriv, &pi); + + /* CAP.NP sometimes indicate the index of the last enabled + * port, at other times, that of the last possible port, so + * determining the maximum port number requires looking at + * both CAP.NP and port_map. + */ + n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); + + host = ata_host_alloc_pinfo(dev, ppi, n_ports); + if (!host) { + ret = -ENOMEM; + goto err7; + } + + host->private_data = hpriv; + + if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) + host->flags |= ATA_HOST_PARALLEL_SCAN; + else + pr_info(KERN_INFO + "ahci: SSS flag set, parallel bus scan disabled\n"); + + if (pi.flags & ATA_FLAG_EM) + ahci_reset_em(host); + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + + ata_port_desc(ap, "mmio %pR", mem); + ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); + + /* set enclosure management message type */ + if (ap->flags & ATA_FLAG_EM) + ap->em_message_type = hpriv->em_msg_type; + + /* disabled/not-implemented port */ + if (!(hpriv->port_map & (1 << i))) + ap->ops = &ata_dummy_port_ops; + } + + ret = ahci_reset_controller(host); + if (ret) + goto err7; + + ahci_init_controller(host); + ahci_print_info(host, "platform"); + + ret = ata_host_activate(host, sata->irq, ahci_interrupt, IRQF_SHARED, + &ahci_platform_sht); + if (ret) + goto err7; + + platform_set_drvdata(pdev, sata); + + return 0; + + err7: + sata_shutdown_phy(sata->phy); + + err6: + sata_put_phy(sata->phy); + + err5: + clk_disable(sata->clk); + devm_clk_put(dev, sata->clk); + + err4: + clk_disable(sata->sclk); + devm_clk_put(dev, sata->sclk); + + err3: + devm_iounmap(dev, hpriv->mmio); + + err2: + devm_kfree(dev, hpriv); + + err1: + devm_kfree(dev, sata); + + return ret; +} + +static int exynos_sata_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ata_host *host = dev_get_drvdata(dev); + struct exynos_sata *sata = platform_get_drvdata(pdev); + + ata_host_detach(host); + + sata_shutdown_phy(sata->phy); + sata_put_phy(sata->phy); + + return 0; +} + +static const struct of_device_id ahci_of_match[] = { + { .compatible = "samsung,exynos5-sata-ahci", }, +}; + +MODULE_DEVICE_TABLE(of, ahci_of_match); + +static struct platform_driver exynos_sata_driver = { + .probe = exynos_sata_probe, + .remove = exynos_sata_remove, + .driver = { + .name = "ahci-sata", + .owner = THIS_MODULE, + .of_match_table = ahci_of_match, + }, +}; + +module_platform_driver(exynos_sata_driver); + +MODULE_DESCRIPTION("EXYNOS SATA DRIVER"); +MODULE_AUTHOR("Vasanth Ananthan, <vasanth.a@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ata/sata_exynos_phy.c b/drivers/ata/sata_exynos_phy.c new file mode 100644 index 000000000000..300ae456af30 --- /dev/null +++ b/drivers/ata/sata_exynos_phy.c @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS - SATA PHY controller driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/ahci_platform.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/io.h> + +#include <plat/cpu.h> + +#include <mach/irqs.h> +#include <mach/map.h> +#include <mach/regs-pmu.h> +#include <mach/regs-sata.h> + +#include "sata_phy.h" + +#define SATA_TIME_LIMIT 1000 + +static struct i2c_client *i2c_client; + +static struct i2c_driver sataphy_i2c_driver; + +struct exynos_sata_phy { + void __iomem *mmio; + struct resource *mem; + struct clk *clk; +}; + +static bool sata_is_reg(void __iomem *base, u32 reg, u32 checkbit, u32 status) +{ + if ((readl(base + reg) & checkbit) == status) + return true; + else + return false; +} + +static bool wait_for_reg_status(void __iomem *base, u32 reg, u32 checkbit, + u32 status) +{ + u16 time_limit_cnt = 0; + while (!sata_is_reg(base, reg, checkbit, status)) { + if (time_limit_cnt == SATA_TIME_LIMIT) + return false; + udelay(1000); + time_limit_cnt++; + } + return true; +} + +static int sataphy_init(struct sata_phy *phy) +{ + int ret; + u32 val; + + /* Values to be written to enable 40 bits interface */ + u8 buf[] = { 0x3A, 0x0B }; + + struct exynos_sata_phy *sata_phy; + + if (!i2c_client) + return -EPROBE_DEFER; + + sata_phy = (struct exynos_sata_phy *)phy->priv_data; + + clk_prepare_enable(sata_phy->clk); + + if (sata_is_reg(sata_phy->mmio , EXYNOS5_SATA_CTRL0, + CTRL0_P0_PHY_CALIBRATED, CTRL0_P0_PHY_CALIBRATED)) + return 0; + + writel(S5P_PMU_SATA_PHY_CONTROL_EN, EXYNOS5_SATA_PHY_CONTROL); + + val = 0; + writel(val, sata_phy->mmio + EXYNOS5_SATA_RESET); + + val = readl(sata_phy->mmio + EXYNOS5_SATA_RESET); + val |= 0xFF; + writel(val, sata_phy->mmio + EXYNOS5_SATA_RESET); + + val = readl(sata_phy->mmio + EXYNOS5_SATA_RESET); + val |= LINK_RESET; + writel(val, sata_phy->mmio + EXYNOS5_SATA_RESET); + + val = readl(sata_phy->mmio + EXYNOS5_SATA_RESET); + val |= RESET_CMN_RST_N; + writel(val, sata_phy->mmio + EXYNOS5_SATA_RESET); + + val = readl(sata_phy->mmio + EXYNOS5_SATA_PHSATA_CTRLM); + val &= ~PHCTRLM_REF_RATE; + writel(val, sata_phy->mmio + EXYNOS5_SATA_PHSATA_CTRLM); + + /* High speed enable for Gen3 */ + val = readl(sata_phy->mmio + EXYNOS5_SATA_PHSATA_CTRLM); + val |= PHCTRLM_HIGH_SPEED; + writel(val, sata_phy->mmio + EXYNOS5_SATA_PHSATA_CTRLM); + + val = readl(sata_phy->mmio + EXYNOS5_SATA_CTRL0); + val |= CTRL0_P0_PHY_CALIBRATED_SEL | CTRL0_P0_PHY_CALIBRATED; + writel(val, sata_phy->mmio + EXYNOS5_SATA_CTRL0); + + writel(SATA_PHY_GENERATION3, sata_phy->mmio + EXYNOS5_SATA_MODE0); + + ret = i2c_master_send(i2c_client, buf, sizeof(buf)); + if (ret < 0) + return -EINVAL; + + /* release cmu reset */ + val = readl(sata_phy->mmio + EXYNOS5_SATA_RESET); + val &= ~RESET_CMN_RST_N; + writel(val, sata_phy->mmio + EXYNOS5_SATA_RESET); + + val = readl(sata_phy->mmio + EXYNOS5_SATA_RESET); + val |= RESET_CMN_RST_N; + writel(val, sata_phy->mmio + EXYNOS5_SATA_RESET); + + if (wait_for_reg_status(sata_phy->mmio, EXYNOS5_SATA_PHSATA_STATM, + PHSTATM_PLL_LOCKED, 1)) { + return 0; + } + return -EINVAL; +} + +static int sataphy_shutdown(struct sata_phy *phy) +{ + + struct exynos_sata_phy *sata_phy; + + sata_phy = (struct exynos_sata_phy *)phy->priv_data; + + clk_disable(sata_phy->clk); + + return 0; +} + +static int __init sata_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *i2c_id) +{ + i2c_client = client; + return 0; +} + +static int __init sata_phy_probe(struct platform_device *pdev) +{ + struct exynos_sata_phy *sataphy; + struct sata_phy *phy; + struct resource *res; + struct device *dev = &pdev->dev; + int ret = 0; + + phy = kzalloc(sizeof(struct sata_phy), GFP_KERNEL); + if (!phy) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + ret = -ENOMEM; + goto out; + } + + sataphy = kzalloc(sizeof(struct exynos_sata_phy), GFP_KERNEL); + if (!sataphy) { + dev_err(dev, "failed to allocate memory\n"); + ret = -ENOMEM; + goto err0; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "Could not find IO resource\n"); + ret = -EINVAL; + goto err1; + } + + sataphy->mem = devm_request_mem_region(dev, res->start, + resource_size(res), pdev->name); + if (!sataphy->mem) { + dev_err(dev, "Could not request IO resource\n"); + ret = -EINVAL; + goto err1; + } + + sataphy->mmio = + devm_ioremap(dev, res->start, resource_size(res)); + if (!sataphy->mmio) { + dev_err(dev, "failed to remap IO\n"); + ret = -ENOMEM; + goto err2; + } + + sataphy->clk = devm_clk_get(dev, "sata-phy"); + if (IS_ERR(sataphy->clk)) { + dev_err(dev, "failed to get clk for PHY\n"); + ret = PTR_ERR(sataphy->clk); + goto err3; + } + + phy->init = sataphy_init; + phy->shutdown = sataphy_shutdown; + phy->priv_data = (void *)sataphy; + phy->dev = dev; + + ret = sata_add_phy(phy, SATA_PHY_GENERATION3); + if (ret < 0) + goto err4; + + ret = i2c_add_driver(&sataphy_i2c_driver); + if (ret < 0) + goto err5; + + platform_set_drvdata(pdev, phy); + + return ret; + + err5: + sata_remove_phy(phy); + + err4: + clk_disable(sataphy->clk); + devm_clk_put(dev, sataphy->clk); + + err3: + devm_iounmap(dev, sataphy->mmio); + + err2: + devm_release_mem_region(dev, res->start, resource_size(res)); + + err1: + kfree(sataphy); + + err0: + kfree(phy); + + out: + return ret; +} + +static int sata_phy_remove(struct platform_device *pdev) +{ + struct sata_phy *phy; + struct exynos_sata_phy *sataphy; + + phy = platform_get_drvdata(pdev); + + sataphy = (struct exynos_sata_phy *)phy->priv_data; + sata_remove_phy(phy); + + kfree(sataphy); + kfree(phy); + + return 0; +} + +static const struct of_device_id sata_phy_of_match[] = { + { .compatible = "samsung,exynos5-sata-phy", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, sata_phy_of_match); + +static const struct i2c_device_id phy_i2c_device_match[] = { + { "sata-phy", 0 }, + {}, +}; + +MODULE_DEVICE_TABLE(of, phy_i2c_device_match); + +static const struct of_device_id phy_i2c_driver_match[] = { + { .compatible = "sata-phy", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, phy_i2c_driver_match); + +static struct platform_driver sata_phy_driver = { + .probe = sata_phy_probe, + .remove = sata_phy_remove, + .driver = { + .name = "sata-phy", + .owner = THIS_MODULE, + .of_match_table = sata_phy_of_match, + }, +}; + +static struct i2c_driver sataphy_i2c_driver = { + .driver = { + .name = "sata-phy-i2c", + .owner = THIS_MODULE, + .of_match_table = phy_i2c_driver_match, + }, + .probe = sata_i2c_probe, + .id_table = phy_i2c_device_match, +}; + +module_platform_driver(sata_phy_driver); + +MODULE_DESCRIPTION("EXYNOS SATA PHY DRIVER"); +MODULE_AUTHOR("Vasanth Ananthan, <vasanth.a@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ata/sata_phy.c b/drivers/ata/sata_phy.c new file mode 100644 index 000000000000..e5631a97951f --- /dev/null +++ b/drivers/ata/sata_phy.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS - SATA utility framework. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/err.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/list.h> +#include "sata_phy.h" + +static LIST_HEAD(phy_list); +static DEFINE_SPINLOCK(phy_lock); + +struct sata_phy *sata_get_phy(enum sata_phy_type type) +{ + struct sata_phy *x = NULL; + unsigned long flag; + + if (list_empty(&phy_list)) + return x; + + spin_lock_irqsave(&phy_lock, flag); + + list_for_each_entry(x, &phy_list, head) { + if (x->type == type) { + get_device(x->dev); + break; + } + } + + spin_unlock_irqrestore(&phy_lock, flag); + return x; +} +EXPORT_SYMBOL(sata_get_phy); + +int sata_add_phy(struct sata_phy *phy, enum sata_phy_type type) +{ + unsigned long flag; + unsigned int ret = -EINVAL; + struct sata_phy *x; + + spin_lock_irqsave(&phy_lock, flag); + + if (!phy) + return ret; + + list_for_each_entry(x, &phy_list, head) { + if (x->type == type) { + dev_err(phy->dev, "transceiver type already exists\n"); + goto out; + } + } + phy->type = type; + list_add_tail(&phy->head, &phy_list); + ret = 0; + + out: + spin_unlock_irqrestore(&phy_lock, flag); + return ret; +} +EXPORT_SYMBOL(sata_add_phy); + +void sata_remove_phy(struct sata_phy *phy) +{ + unsigned long flag; + struct sata_phy *x; + + spin_lock_irqsave(&phy_lock, flag); + + if (!phy) + return; + + list_for_each_entry(x, &phy_list, head) { + if (x->type == phy->type) + list_del(&phy->head); + } + + spin_unlock_irqrestore(&phy_lock, flag); +} +EXPORT_SYMBOL(sata_remove_phy); + +void sata_put_phy(struct sata_phy *phy) +{ + unsigned long flag; + + spin_lock_irqsave(&phy_lock, flag); + + if (!phy) + return; + + put_device(phy->dev); + spin_unlock_irqrestore(&phy_lock, flag); + +} +EXPORT_SYMBOL(sata_put_phy); diff --git a/drivers/ata/sata_phy.h b/drivers/ata/sata_phy.h new file mode 100644 index 000000000000..dc38683052e3 --- /dev/null +++ b/drivers/ata/sata_phy.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS - SATA utility framework definitions. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +enum sata_phy_type { + SATA_PHY_GENERATION1, + SATA_PHY_GENERATION2, + SATA_PHY_GENERATION3, +}; + +struct sata_phy { + int (*init) (struct sata_phy *); + int (*shutdown) (struct sata_phy *); + struct device *dev; + void *priv_data; + enum sata_phy_type type; + struct list_head head; +}; + +static inline int sata_init_phy(struct sata_phy *x) +{ + if (x && x->init) + return x->init(x); + + return -EINVAL; +} + +static inline void sata_shutdown_phy(struct sata_phy *x) +{ + if (x && x->shutdown) + x->shutdown(x); +} + +struct sata_phy *sata_get_phy(enum sata_phy_type); +int sata_add_phy(struct sata_phy *, enum sata_phy_type); +void sata_remove_phy(struct sata_phy *); +void sata_put_phy(struct sata_phy *); diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c new file mode 100644 index 000000000000..dacae3ba381f --- /dev/null +++ b/drivers/bus/arm-cci.c @@ -0,0 +1,947 @@ +/* + * CCI cache coherent interconnect driver + * + * Copyright (C) 2013 ARM Ltd. + * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/arm-cci.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of_address.h> +#include <linux/slab.h> + +#include <asm/cacheflush.h> +#include <asm/irq_regs.h> +#include <asm/pmu.h> +#include <asm/smp_plat.h> + +#define DRIVER_NAME "CCI" + +#define CCI_PORT_CTRL 0x0 +#define CCI_CTRL_STATUS 0xc + +#define CCI_ENABLE_SNOOP_REQ 0x1 +#define CCI_ENABLE_DVM_REQ 0x2 +#define CCI_ENABLE_REQ (CCI_ENABLE_SNOOP_REQ | CCI_ENABLE_DVM_REQ) + +struct cci_nb_ports { + unsigned int nb_ace; + unsigned int nb_ace_lite; +}; + +enum cci_ace_port_type { + ACE_INVALID_PORT = 0x0, + ACE_PORT, + ACE_LITE_PORT, +}; + +struct cci_ace_port { + void __iomem *base; + unsigned long phys; + enum cci_ace_port_type type; + struct device_node *dn; +}; + +static struct cci_ace_port *ports; +static unsigned int nb_cci_ports; + +static void __iomem *cci_ctrl_base; +static unsigned long cci_ctrl_phys; + +#ifdef CONFIG_HW_PERF_EVENTS + +static void __iomem *cci_pmu_base; + +#define CCI400_PMCR 0x0100 + +#define CCI400_PMU_CYCLE_CNTR_BASE 0x0000 +#define CCI400_PMU_CNTR_BASE(idx) (CCI400_PMU_CYCLE_CNTR_BASE + (idx) * 0x1000) + +#define CCI400_PMCR_CEN 0x00000001 +#define CCI400_PMCR_RST 0x00000002 +#define CCI400_PMCR_CCR 0x00000004 +#define CCI400_PMCR_CCD 0x00000008 +#define CCI400_PMCR_EX 0x00000010 +#define CCI400_PMCR_DP 0x00000020 +#define CCI400_PMCR_NCNT_MASK 0x0000F800 +#define CCI400_PMCR_NCNT_SHIFT 11 + +#define CCI400_PMU_EVT_SEL 0x000 +#define CCI400_PMU_CNTR 0x004 +#define CCI400_PMU_CNTR_CTRL 0x008 +#define CCI400_PMU_OVERFLOW 0x00C + +#define CCI400_PMU_OVERFLOW_FLAG 1 + +enum cci400_perf_events { + CCI400_PMU_CYCLES = 0xFF +}; + +#define CCI400_PMU_EVENT_MASK 0xff +#define CCI400_PMU_EVENT_SOURCE(event) ((event >> 5) & 0x7) +#define CCI400_PMU_EVENT_CODE(event) (event & 0x1f) + +#define CCI400_PMU_EVENT_SOURCE_S0 0 +#define CCI400_PMU_EVENT_SOURCE_S4 4 +#define CCI400_PMU_EVENT_SOURCE_M0 5 +#define CCI400_PMU_EVENT_SOURCE_M2 7 + +#define CCI400_PMU_EVENT_SLAVE_MIN 0x0 +#define CCI400_PMU_EVENT_SLAVE_MAX 0x13 + +#define CCI400_PMU_EVENT_MASTER_MIN 0x14 +#define CCI400_PMU_EVENT_MASTER_MAX 0x1A + +#define CCI400_PMU_MAX_HW_EVENTS 5 /* CCI PMU has 4 counters + 1 cycle counter */ + +#define CCI400_PMU_CYCLE_COUNTER_IDX 0 +#define CCI400_PMU_COUNTER0_IDX 1 +#define CCI400_PMU_COUNTER_LAST(cci_pmu) (CCI400_PMU_CYCLE_COUNTER_IDX + cci_pmu->num_events - 1) + + +static struct perf_event *events[CCI400_PMU_MAX_HW_EVENTS]; +static unsigned long used_mask[BITS_TO_LONGS(CCI400_PMU_MAX_HW_EVENTS)]; +static struct pmu_hw_events cci_hw_events = { + .events = events, + .used_mask = used_mask, +}; + +static int cci_pmu_validate_hw_event(u8 hw_event) +{ + u8 ev_source = CCI400_PMU_EVENT_SOURCE(hw_event); + u8 ev_code = CCI400_PMU_EVENT_CODE(hw_event); + + if (ev_source <= CCI400_PMU_EVENT_SOURCE_S4 && + ev_code <= CCI400_PMU_EVENT_SLAVE_MAX) + return hw_event; + else if (CCI400_PMU_EVENT_SOURCE_M0 <= ev_source && + ev_source <= CCI400_PMU_EVENT_SOURCE_M2 && + CCI400_PMU_EVENT_MASTER_MIN <= ev_code && + ev_code <= CCI400_PMU_EVENT_MASTER_MAX) + return hw_event; + + return -EINVAL; +} + +static inline int cci_pmu_counter_is_valid(struct arm_pmu *cci_pmu, int idx) +{ + return CCI400_PMU_CYCLE_COUNTER_IDX <= idx && + idx <= CCI400_PMU_COUNTER_LAST(cci_pmu); +} + +static inline u32 cci_pmu_read_register(int idx, unsigned int offset) +{ + return readl_relaxed(cci_pmu_base + CCI400_PMU_CNTR_BASE(idx) + offset); +} + +static inline void cci_pmu_write_register(u32 value, int idx, unsigned int offset) +{ + return writel_relaxed(value, cci_pmu_base + CCI400_PMU_CNTR_BASE(idx) + offset); +} + +static inline void cci_pmu_disable_counter(int idx) +{ + cci_pmu_write_register(0, idx, CCI400_PMU_CNTR_CTRL); +} + +static inline void cci_pmu_enable_counter(int idx) +{ + cci_pmu_write_register(1, idx, CCI400_PMU_CNTR_CTRL); +} + +static inline void cci_pmu_select_event(int idx, unsigned long event) +{ + event &= CCI400_PMU_EVENT_MASK; + cci_pmu_write_register(event, idx, CCI400_PMU_EVT_SEL); +} + +static u32 cci_pmu_get_max_counters(void) +{ + u32 n_cnts = (readl_relaxed(cci_ctrl_base + CCI400_PMCR) & + CCI400_PMCR_NCNT_MASK) >> CCI400_PMCR_NCNT_SHIFT; + + /* add 1 for cycle counter */ + return n_cnts + 1; +} + +static struct pmu_hw_events *cci_pmu_get_hw_events(void) +{ + return &cci_hw_events; +} + +static int cci_pmu_get_event_idx(struct pmu_hw_events *hw, struct perf_event *event) +{ + struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); + struct hw_perf_event *hw_event = &event->hw; + unsigned long cci_event = hw_event->config_base & CCI400_PMU_EVENT_MASK; + int idx; + + if (cci_event == CCI400_PMU_CYCLES) { + if (test_and_set_bit(CCI400_PMU_CYCLE_COUNTER_IDX, hw->used_mask)) + return -EAGAIN; + + return CCI400_PMU_CYCLE_COUNTER_IDX; + } + + for (idx = CCI400_PMU_COUNTER0_IDX; idx <= CCI400_PMU_COUNTER_LAST(cci_pmu); ++idx) { + if (!test_and_set_bit(idx, hw->used_mask)) + return idx; + } + + /* No counters available */ + return -EAGAIN; +} + +static int cci_pmu_map_event(struct perf_event *event) +{ + int mapping; + u8 config = event->attr.config & CCI400_PMU_EVENT_MASK; + + if (event->attr.type < PERF_TYPE_MAX) + return -ENOENT; + + /* 0xff is used to represent CCI Cycles */ + if (config == 0xff) + mapping = config; + else + mapping = cci_pmu_validate_hw_event(config); + + return mapping; +} + +static int cci_pmu_request_irq(struct arm_pmu *cci_pmu, irq_handler_t handler) +{ + int irq, err, i = 0; + struct platform_device *pmu_device = cci_pmu->plat_device; + + if (unlikely(!pmu_device)) + return -ENODEV; + + /* CCI exports 6 interrupts - 1 nERRORIRQ + 5 nEVNTCNTOVERFLOW (PMU) + nERRORIRQ will be handled by secure firmware on TC2. So we + assume that all CCI interrupts listed in the linux device + tree are PMU interrupts. + + The following code should then be able to handle different routing + of the CCI PMU interrupts. + */ + while ((irq = platform_get_irq(pmu_device, i)) > 0) { + err = request_irq(irq, handler, 0, "arm-cci-pmu", cci_pmu); + if (err) { + dev_err(&pmu_device->dev, "unable to request IRQ%d for ARM CCI PMU counters\n", + irq); + return err; + } + i++; + } + + return 0; +} + +static irqreturn_t cci_pmu_handle_irq(int irq_num, void *dev) +{ + struct arm_pmu *cci_pmu = (struct arm_pmu *)dev; + struct pmu_hw_events *events = cci_pmu->get_hw_events(); + struct perf_sample_data data; + struct pt_regs *regs; + int idx; + + regs = get_irq_regs(); + + /* Iterate over counters and update the corresponding perf events. + This should work regardless of whether we have per-counter overflow + interrupt or a combined overflow interrupt. */ + for (idx = CCI400_PMU_CYCLE_COUNTER_IDX; idx <= CCI400_PMU_COUNTER_LAST(cci_pmu); idx++) { + struct perf_event *event = events->events[idx]; + struct hw_perf_event *hw_counter; + + if (!event) + continue; + + hw_counter = &event->hw; + + /* Did this counter overflow? */ + if (!(cci_pmu_read_register(idx, CCI400_PMU_OVERFLOW) & CCI400_PMU_OVERFLOW_FLAG)) + continue; + cci_pmu_write_register(CCI400_PMU_OVERFLOW_FLAG, idx, CCI400_PMU_OVERFLOW); + + armpmu_event_update(event); + perf_sample_data_init(&data, 0, hw_counter->last_period); + if (!armpmu_event_set_period(event)) + continue; + + if (perf_event_overflow(event, &data, regs)) + cci_pmu->disable(event); + } + + irq_work_run(); + return IRQ_HANDLED; +} + +static void cci_pmu_free_irq(struct arm_pmu *cci_pmu) +{ + int irq, i = 0; + struct platform_device *pmu_device = cci_pmu->plat_device; + + while ((irq = platform_get_irq(pmu_device, i)) > 0) { + free_irq(irq, cci_pmu); + i++; + } +} + +static void cci_pmu_enable_event(struct perf_event *event) +{ + unsigned long flags; + struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); + struct pmu_hw_events *events = cci_pmu->get_hw_events(); + struct hw_perf_event *hw_counter = &event->hw; + int idx = hw_counter->idx; + + if (unlikely(!cci_pmu_counter_is_valid(cci_pmu, idx))) { + dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); + return; + } + + raw_spin_lock_irqsave(&events->pmu_lock, flags); + + /* Configure the event to count, unless you are counting cycles */ + if (idx != CCI400_PMU_CYCLE_COUNTER_IDX) + cci_pmu_select_event(idx, hw_counter->config_base); + + cci_pmu_enable_counter(idx); + + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); +} + +static void cci_pmu_disable_event(struct perf_event *event) +{ + unsigned long flags; + struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); + struct pmu_hw_events *events = cci_pmu->get_hw_events(); + struct hw_perf_event *hw_counter = &event->hw; + int idx = hw_counter->idx; + + if (unlikely(!cci_pmu_counter_is_valid(cci_pmu, idx))) { + dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); + return; + } + + raw_spin_lock_irqsave(&events->pmu_lock, flags); + + cci_pmu_disable_counter(idx); + + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); +} + +static void cci_pmu_start(struct arm_pmu *cci_pmu) +{ + u32 val; + unsigned long flags; + struct pmu_hw_events *events = cci_pmu->get_hw_events(); + + raw_spin_lock_irqsave(&events->pmu_lock, flags); + + /* Enable all the PMU counters. */ + val = readl(cci_ctrl_base + CCI400_PMCR) | CCI400_PMCR_CEN; + writel(val, cci_ctrl_base + CCI400_PMCR); + + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); +} + +static void cci_pmu_stop(struct arm_pmu *cci_pmu) +{ + u32 val; + unsigned long flags; + struct pmu_hw_events *events = cci_pmu->get_hw_events(); + + raw_spin_lock_irqsave(&events->pmu_lock, flags); + + /* Disable all the PMU counters. */ + val = readl(cci_ctrl_base + CCI400_PMCR) & ~CCI400_PMCR_CEN; + writel(val, cci_ctrl_base + CCI400_PMCR); + + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); +} + +static u32 cci_pmu_read_counter(struct perf_event *event) +{ + struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); + struct hw_perf_event *hw_counter = &event->hw; + int idx = hw_counter->idx; + u32 value; + + if (unlikely(!cci_pmu_counter_is_valid(cci_pmu, idx))) { + dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); + return 0; + } + value = cci_pmu_read_register(idx, CCI400_PMU_CNTR); + + return value; +} + +static void cci_pmu_write_counter(struct perf_event *event, u32 value) +{ + struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); + struct hw_perf_event *hw_counter = &event->hw; + int idx = hw_counter->idx; + + if (unlikely(!cci_pmu_counter_is_valid(cci_pmu, idx))) + dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); + else + cci_pmu_write_register(value, idx, CCI400_PMU_CNTR); +} + +static struct arm_pmu cci_pmu = { + .name = DRIVER_NAME, + .max_period = (1LLU << 32) - 1, + .get_hw_events = cci_pmu_get_hw_events, + .get_event_idx = cci_pmu_get_event_idx, + .map_event = cci_pmu_map_event, + .request_irq = cci_pmu_request_irq, + .handle_irq = cci_pmu_handle_irq, + .free_irq = cci_pmu_free_irq, + .enable = cci_pmu_enable_event, + .disable = cci_pmu_disable_event, + .start = cci_pmu_start, + .stop = cci_pmu_stop, + .read_counter = cci_pmu_read_counter, + .write_counter = cci_pmu_write_counter, +}; + +static int cci_pmu_probe(struct platform_device *pdev) +{ + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + cci_pmu_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(cci_pmu_base)) + return PTR_ERR(cci_pmu_base); + + cci_pmu.plat_device = pdev; + cci_pmu.num_events = cci_pmu_get_max_counters(); + raw_spin_lock_init(&cci_hw_events.pmu_lock); + cpumask_setall(&cci_pmu.valid_cpus); + + return armpmu_register(&cci_pmu, -1); +} + +static const struct of_device_id arm_cci_pmu_matches[] = { + {.compatible = "arm,cci-400-pmu"}, + {}, +}; + +static struct platform_driver cci_pmu_platform_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = arm_cci_pmu_matches, + }, + .probe = cci_pmu_probe, +}; + +static int __init cci_pmu_init(void) +{ + if (platform_driver_register(&cci_pmu_platform_driver)) + WARN(1, "unable to register CCI platform driver\n"); + return 0; +} + +#else + +static int __init cci_pmu_init(void) +{ + return 0; +} + +#endif /* CONFIG_HW_PERF_EVENTS */ + +struct cpu_port { + u64 mpidr; + u32 port; +}; + +/* + * Use the port MSB as valid flag, shift can be made dynamic + * by computing number of bits required for port indexes. + * Code disabling CCI cpu ports runs with D-cache invalidated + * and SCTLR bit clear so data accesses must be kept to a minimum + * to improve performance; for now shift is left static to + * avoid one more data access while disabling the CCI port. + */ +#define PORT_VALID_SHIFT 31 +#define PORT_VALID (0x1 << PORT_VALID_SHIFT) + +static inline void init_cpu_port(struct cpu_port *port, u32 index, u64 mpidr) +{ + port->port = PORT_VALID | index; + port->mpidr = mpidr; +} + +static inline bool cpu_port_is_valid(struct cpu_port *port) +{ + return !!(port->port & PORT_VALID); +} + +static inline bool cpu_port_match(struct cpu_port *port, u64 mpidr) +{ + return port->mpidr == (mpidr & MPIDR_HWID_BITMASK); +} + +static struct cpu_port cpu_port[NR_CPUS]; + +/** + * __cci_ace_get_port - Function to retrieve the port index connected to + * a cpu or device. + * + * @dn: device node of the device to look-up + * @type: port type + * + * Return value: + * - CCI port index if success + * - -ENODEV if failure + */ +static int __cci_ace_get_port(struct device_node *dn, int type) +{ + int i; + bool ace_match; + struct device_node *cci_portn; + + cci_portn = of_parse_phandle(dn, "cci-control-port", 0); + for (i = 0; i < nb_cci_ports; i++) { + ace_match = ports[i].type == type; + if (ace_match && cci_portn == ports[i].dn) + return i; + } + return -ENODEV; +} + +int cci_ace_get_port(struct device_node *dn) +{ + return __cci_ace_get_port(dn, ACE_LITE_PORT); +} +EXPORT_SYMBOL_GPL(cci_ace_get_port); + +static void __init cci_ace_init_ports(void) +{ + int port, ac, cpu; + u64 hwid; + const u32 *cell; + struct device_node *cpun, *cpus; + + cpus = of_find_node_by_path("/cpus"); + if (WARN(!cpus, "Missing cpus node, bailing out\n")) + return; + + if (WARN_ON(of_property_read_u32(cpus, "#address-cells", &ac))) + ac = of_n_addr_cells(cpus); + + /* + * Port index look-up speeds up the function disabling ports by CPU, + * since the logical to port index mapping is done once and does + * not change after system boot. + * The stashed index array is initialized for all possible CPUs + * at probe time. + */ + for_each_child_of_node(cpus, cpun) { + if (of_node_cmp(cpun->type, "cpu")) + continue; + cell = of_get_property(cpun, "reg", NULL); + if (WARN(!cell, "%s: missing reg property\n", cpun->full_name)) + continue; + + hwid = of_read_number(cell, ac); + cpu = get_logical_index(hwid & MPIDR_HWID_BITMASK); + + if (cpu < 0 || !cpu_possible(cpu)) + continue; + port = __cci_ace_get_port(cpun, ACE_PORT); + if (port < 0) + continue; + + init_cpu_port(&cpu_port[cpu], port, cpu_logical_map(cpu)); + } + + for_each_possible_cpu(cpu) { + WARN(!cpu_port_is_valid(&cpu_port[cpu]), + "CPU %u does not have an associated CCI port\n", + cpu); + } +} +/* + * Functions to enable/disable a CCI interconnect slave port + * + * They are called by low-level power management code to disable slave + * interfaces snoops and DVM broadcast. + * Since they may execute with cache data allocation disabled and + * after the caches have been cleaned and invalidated the functions provide + * no explicit locking since they may run with D-cache disabled, so normal + * cacheable kernel locks based on ldrex/strex may not work. + * Locking has to be provided by BSP implementations to ensure proper + * operations. + */ + +/** + * cci_port_control() - function to control a CCI port + * + * @port: index of the port to setup + * @enable: if true enables the port, if false disables it + */ +static void notrace cci_port_control(unsigned int port, bool enable) +{ + void __iomem *base = ports[port].base; + + writel_relaxed(enable ? CCI_ENABLE_REQ : 0, base + CCI_PORT_CTRL); + /* + * This function is called from power down procedures + * and must not execute any instruction that might + * cause the processor to be put in a quiescent state + * (eg wfi). Hence, cpu_relax() can not be added to this + * read loop to optimize power, since it might hide possibly + * disruptive operations. + */ + while (readl_relaxed(cci_ctrl_base + CCI_CTRL_STATUS) & 0x1) + ; +} + +/** + * cci_disable_port_by_cpu() - function to disable a CCI port by CPU + * reference + * + * @mpidr: mpidr of the CPU whose CCI port should be disabled + * + * Disabling a CCI port for a CPU implies disabling the CCI port + * controlling that CPU cluster. Code disabling CPU CCI ports + * must make sure that the CPU running the code is the last active CPU + * in the cluster ie all other CPUs are quiescent in a low power state. + * + * Return: + * 0 on success + * -ENODEV on port look-up failure + */ +int notrace cci_disable_port_by_cpu(u64 mpidr) +{ + int cpu; + bool is_valid; + for (cpu = 0; cpu < nr_cpu_ids; cpu++) { + is_valid = cpu_port_is_valid(&cpu_port[cpu]); + if (is_valid && cpu_port_match(&cpu_port[cpu], mpidr)) { + cci_port_control(cpu_port[cpu].port, false); + return 0; + } + } + return -ENODEV; +} +EXPORT_SYMBOL_GPL(cci_disable_port_by_cpu); + +/** + * cci_enable_port_for_self() - enable a CCI port for calling CPU + * + * Enabling a CCI port for the calling CPU implies enabling the CCI + * port controlling that CPU's cluster. Caller must make sure that the + * CPU running the code is the first active CPU in the cluster and all + * other CPUs are quiescent in a low power state or waiting for this CPU + * to complete the CCI initialization. + * + * Because this is called when the MMU is still off and with no stack, + * the code must be position independent and ideally rely on callee + * clobbered registers only. To achieve this we must code this function + * entirely in assembler. + * + * On success this returns with the proper CCI port enabled. In case of + * any failure this never returns as the inability to enable the CCI is + * fatal and there is no possible recovery at this stage. + */ +asmlinkage void __naked cci_enable_port_for_self(void) +{ + asm volatile ("\n" + +" mrc p15, 0, r0, c0, c0, 5 @ get MPIDR value \n" +" and r0, r0, #"__stringify(MPIDR_HWID_BITMASK)" \n" +" adr r1, 5f \n" +" ldr r2, [r1] \n" +" add r1, r1, r2 @ &cpu_port \n" +" add ip, r1, %[sizeof_cpu_port] \n" + + /* Loop over the cpu_port array looking for a matching MPIDR */ +"1: ldr r2, [r1, %[offsetof_cpu_port_mpidr_lsb]] \n" +" cmp r2, r0 @ compare MPIDR \n" +" bne 2f \n" + + /* Found a match, now test port validity */ +" ldr r3, [r1, %[offsetof_cpu_port_port]] \n" +" tst r3, #"__stringify(PORT_VALID)" \n" +" bne 3f \n" + + /* no match, loop with the next cpu_port entry */ +"2: add r1, r1, %[sizeof_struct_cpu_port] \n" +" cmp r1, ip @ done? \n" +" blo 1b \n" + + /* CCI port not found -- cheaply try to stall this CPU */ +"cci_port_not_found: \n" +" wfi \n" +" wfe \n" +" b cci_port_not_found \n" + + /* Use matched port index to look up the corresponding ports entry */ +"3: bic r3, r3, #"__stringify(PORT_VALID)" \n" +" adr r0, 6f \n" +" ldmia r0, {r1, r2} \n" +" sub r1, r1, r0 @ virt - phys \n" +" ldr r0, [r0, r2] @ *(&ports) \n" +" mov r2, %[sizeof_struct_ace_port] \n" +" mla r0, r2, r3, r0 @ &ports[index] \n" +" sub r0, r0, r1 @ virt_to_phys() \n" + + /* Enable the CCI port */ +" ldr r0, [r0, %[offsetof_port_phys]] \n" +" mov r3, %[cci_enable_req]\n" +" str r3, [r0, #"__stringify(CCI_PORT_CTRL)"] \n" + + /* poll the status reg for completion */ +" adr r1, 7f \n" +" ldr r0, [r1] \n" +" ldr r0, [r0, r1] @ cci_ctrl_base \n" +"4: ldr r1, [r0, #"__stringify(CCI_CTRL_STATUS)"] \n" +" tst r1, %[cci_control_status_bits] \n" +" bne 4b \n" + +" mov r0, #0 \n" +" bx lr \n" + +" .align 2 \n" +"5: .word cpu_port - . \n" +"6: .word . \n" +" .word ports - 6b \n" +"7: .word cci_ctrl_phys - . \n" + : : + [sizeof_cpu_port] "i" (sizeof(cpu_port)), + [cci_enable_req] "i" cpu_to_le32(CCI_ENABLE_REQ), + [cci_control_status_bits] "i" cpu_to_le32(1), +#ifndef __ARMEB__ + [offsetof_cpu_port_mpidr_lsb] "i" (offsetof(struct cpu_port, mpidr)), +#else + [offsetof_cpu_port_mpidr_lsb] "i" (offsetof(struct cpu_port, mpidr)+4), +#endif + [offsetof_cpu_port_port] "i" (offsetof(struct cpu_port, port)), + [sizeof_struct_cpu_port] "i" (sizeof(struct cpu_port)), + [sizeof_struct_ace_port] "i" (sizeof(struct cci_ace_port)), + [offsetof_port_phys] "i" (offsetof(struct cci_ace_port, phys)) ); + + unreachable(); +} + +/** + * __cci_control_port_by_device() - function to control a CCI port by device + * reference + * + * @dn: device node pointer of the device whose CCI port should be + * controlled + * @enable: if true enables the port, if false disables it + * + * Return: + * 0 on success + * -ENODEV on port look-up failure + */ +int notrace __cci_control_port_by_device(struct device_node *dn, bool enable) +{ + int port; + + if (!dn) + return -ENODEV; + + port = __cci_ace_get_port(dn, ACE_LITE_PORT); + if (WARN_ONCE(port < 0, "node %s ACE lite port look-up failure\n", + dn->full_name)) + return -ENODEV; + cci_port_control(port, enable); + return 0; +} +EXPORT_SYMBOL_GPL(__cci_control_port_by_device); + +/** + * __cci_control_port_by_index() - function to control a CCI port by port index + * + * @port: port index previously retrieved with cci_ace_get_port() + * @enable: if true enables the port, if false disables it + * + * Return: + * 0 on success + * -ENODEV on port index out of range + * -EPERM if operation carried out on an ACE PORT + */ +int notrace __cci_control_port_by_index(u32 port, bool enable) +{ + if (port >= nb_cci_ports || ports[port].type == ACE_INVALID_PORT) + return -ENODEV; + /* + * CCI control for ports connected to CPUS is extremely fragile + * and must be made to go through a specific and controlled + * interface (ie cci_disable_port_by_cpu(); control by general purpose + * indexing is therefore disabled for ACE ports. + */ + if (ports[port].type == ACE_PORT) + return -EPERM; + + cci_port_control(port, enable); + return 0; +} +EXPORT_SYMBOL_GPL(__cci_control_port_by_index); + +static const struct cci_nb_ports cci400_ports = { + .nb_ace = 2, + .nb_ace_lite = 3 +}; + +static const struct of_device_id arm_cci_matches[] = { + {.compatible = "arm,cci-400", .data = &cci400_ports }, + {}, +}; + +static const struct of_device_id arm_cci_ctrl_if_matches[] = { + {.compatible = "arm,cci-400-ctrl-if", }, + {}, +}; + +static int __init cci_probe(void) +{ + struct cci_nb_ports const *cci_config; + int ret, i, nb_ace = 0, nb_ace_lite = 0; + struct device_node *np, *cp; + struct resource res; + const char *match_str; + bool is_ace; + + np = of_find_matching_node(NULL, arm_cci_matches); + if (!np) + return -ENODEV; + + cci_config = of_match_node(arm_cci_matches, np)->data; + if (!cci_config) + return -ENODEV; + + nb_cci_ports = cci_config->nb_ace + cci_config->nb_ace_lite; + + ports = kcalloc(sizeof(*ports), nb_cci_ports, GFP_KERNEL); + if (!ports) + return -ENOMEM; + + ret = of_address_to_resource(np, 0, &res); + if (!ret) { + cci_ctrl_base = ioremap(res.start, resource_size(&res)); + cci_ctrl_phys = res.start; + } + if (ret || !cci_ctrl_base) { + WARN(1, "unable to ioremap CCI ctrl\n"); + ret = -ENXIO; + goto memalloc_err; + } + + for_each_child_of_node(np, cp) { + if (!of_match_node(arm_cci_ctrl_if_matches, cp)) + continue; + + i = nb_ace + nb_ace_lite; + + if (i >= nb_cci_ports) + break; + + if (of_property_read_string(cp, "interface-type", + &match_str)) { + WARN(1, "node %s missing interface-type property\n", + cp->full_name); + continue; + } + is_ace = strcmp(match_str, "ace") == 0; + if (!is_ace && strcmp(match_str, "ace-lite")) { + WARN(1, "node %s containing invalid interface-type property, skipping it\n", + cp->full_name); + continue; + } + + ret = of_address_to_resource(cp, 0, &res); + if (!ret) { + ports[i].base = ioremap(res.start, resource_size(&res)); + ports[i].phys = res.start; + } + if (ret || !ports[i].base) { + WARN(1, "unable to ioremap CCI port %d\n", i); + continue; + } + + if (is_ace) { + if (WARN_ON(nb_ace >= cci_config->nb_ace)) + continue; + ports[i].type = ACE_PORT; + ++nb_ace; + } else { + if (WARN_ON(nb_ace_lite >= cci_config->nb_ace_lite)) + continue; + ports[i].type = ACE_LITE_PORT; + ++nb_ace_lite; + } + ports[i].dn = cp; + } + + /* initialize a stashed array of ACE ports to speed-up look-up */ + cci_ace_init_ports(); + + /* + * Multi-cluster systems may need this data when non-coherent, during + * cluster power-up/power-down. Make sure it reaches main memory. + */ + sync_cache_w(&cci_ctrl_base); + sync_cache_w(&cci_ctrl_phys); + sync_cache_w(&ports); + sync_cache_w(&cpu_port); + __sync_cache_range_w(ports, sizeof(*ports) * nb_cci_ports); + pr_info("ARM CCI driver probed\n"); + return 0; + +memalloc_err: + + kfree(ports); + return ret; +} + +static int cci_init_status = -EAGAIN; +static DEFINE_MUTEX(cci_probing); + +static int __init cci_init(void) +{ + if (cci_init_status != -EAGAIN) + return cci_init_status; + + mutex_lock(&cci_probing); + if (cci_init_status == -EAGAIN) + cci_init_status = cci_probe(); + mutex_unlock(&cci_probing); + return cci_init_status; +} + +/* + * To sort out early init calls ordering a helper function is provided to + * check if the CCI driver has beed initialized. Function check if the driver + * has been initialized, if not it calls the init function that probes + * the driver and updates the return value. + */ +bool __init cci_probed(void) +{ + return cci_init() == 0; +} +EXPORT_SYMBOL_GPL(cci_probed); + +early_initcall(cci_init); +core_initcall(cci_pmu_init); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ARM CCI support"); diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index 362f12dcd944..b7cd4fe54455 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c @@ -38,7 +38,7 @@ static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw, u32 mdiv, pdiv, sdiv, pll_con; u64 fvco = parent_rate; - pll_con = __raw_readl(pll->con_reg); + pll_con = readl_relaxed(pll->con_reg); mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK; pdiv = (pll_con >> PLL35XX_PDIV_SHIFT) & PLL35XX_PDIV_MASK; sdiv = (pll_con >> PLL35XX_SDIV_SHIFT) & PLL35XX_SDIV_MASK; @@ -115,8 +115,8 @@ static unsigned long samsung_pll36xx_recalc_rate(struct clk_hw *hw, s16 kdiv; u64 fvco = parent_rate; - pll_con0 = __raw_readl(pll->con_reg); - pll_con1 = __raw_readl(pll->con_reg + 4); + pll_con0 = readl_relaxed(pll->con_reg); + pll_con1 = readl_relaxed(pll->con_reg + 4); mdiv = (pll_con0 >> PLL36XX_MDIV_SHIFT) & PLL36XX_MDIV_MASK; pdiv = (pll_con0 >> PLL36XX_PDIV_SHIFT) & PLL36XX_PDIV_MASK; sdiv = (pll_con0 >> PLL36XX_SDIV_SHIFT) & PLL36XX_SDIV_MASK; @@ -194,7 +194,7 @@ static unsigned long samsung_pll45xx_recalc_rate(struct clk_hw *hw, u32 mdiv, pdiv, sdiv, pll_con; u64 fvco = parent_rate; - pll_con = __raw_readl(pll->con_reg); + pll_con = readl_relaxed(pll->con_reg); mdiv = (pll_con >> PLL45XX_MDIV_SHIFT) & PLL45XX_MDIV_MASK; pdiv = (pll_con >> PLL45XX_PDIV_SHIFT) & PLL45XX_PDIV_MASK; sdiv = (pll_con >> PLL45XX_SDIV_SHIFT) & PLL45XX_SDIV_MASK; @@ -279,8 +279,8 @@ static unsigned long samsung_pll46xx_recalc_rate(struct clk_hw *hw, u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1, shift; u64 fvco = parent_rate; - pll_con0 = __raw_readl(pll->con_reg); - pll_con1 = __raw_readl(pll->con_reg + 4); + pll_con0 = readl_relaxed(pll->con_reg); + pll_con1 = readl_relaxed(pll->con_reg + 4); mdiv = (pll_con0 >> PLL46XX_MDIV_SHIFT) & PLL46XX_MDIV_MASK; pdiv = (pll_con0 >> PLL46XX_PDIV_SHIFT) & PLL46XX_PDIV_MASK; sdiv = (pll_con0 >> PLL46XX_SDIV_SHIFT) & PLL46XX_SDIV_MASK; @@ -364,7 +364,7 @@ static unsigned long samsung_pll2550x_recalc_rate(struct clk_hw *hw, u32 r, p, m, s, pll_stat; u64 fvco = parent_rate; - pll_stat = __raw_readl(pll->reg_base + pll->offset * 3); + pll_stat = readl_relaxed(pll->reg_base + pll->offset * 3); r = (pll_stat >> PLL2550X_R_SHIFT) & PLL2550X_R_MASK; if (!r) return 0; diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c index cd3c40ab50f3..e946bbcf4ac9 100644 --- a/drivers/clk/samsung/clk.c +++ b/drivers/clk/samsung/clk.c @@ -31,7 +31,7 @@ static int samsung_clk_suspend(void) unsigned long i; for (i = 0; i < nr_reg_dump; i++, rd++) - rd->value = __raw_readl(reg_base + rd->offset); + rd->value = readl_relaxed(reg_base + rd->offset); return 0; } @@ -42,7 +42,7 @@ static void samsung_clk_resume(void) unsigned long i; for (i = 0; i < nr_reg_dump; i++, rd++) - __raw_writel(rd->value, reg_base + rd->offset); + writel_relaxed(rd->value, reg_base + rd->offset); } static struct syscore_ops samsung_clk_syscore_ops = { diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index 662fcc065821..ad9a2157748e 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -91,7 +91,7 @@ static void exynos4_mct_write(unsigned int value, unsigned long offset) u32 mask; u32 i; - __raw_writel(value, reg_base + offset); + writel_relaxed(value, reg_base + offset); if (likely(offset >= EXYNOS4_MCT_L_BASE(0))) { stat_addr = (offset & ~EXYNOS4_MCT_L_MASK) + MCT_L_WSTAT_OFFSET; @@ -141,8 +141,8 @@ static void exynos4_mct_write(unsigned int value, unsigned long offset) /* Wait maximum 1 ms until written values are applied */ for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++) - if (__raw_readl(reg_base + stat_addr) & mask) { - __raw_writel(mask, reg_base + stat_addr); + if (readl_relaxed(reg_base + stat_addr) & mask) { + writel_relaxed(mask, reg_base + stat_addr); return; } @@ -157,7 +157,7 @@ static void exynos4_mct_frc_start(u32 hi, u32 lo) exynos4_mct_write(lo, EXYNOS4_MCT_G_CNT_L); exynos4_mct_write(hi, EXYNOS4_MCT_G_CNT_U); - reg = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON); + reg = readl_relaxed(reg_base + EXYNOS4_MCT_G_TCON); reg |= MCT_G_TCON_START; exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON); } @@ -165,12 +165,12 @@ static void exynos4_mct_frc_start(u32 hi, u32 lo) static cycle_t exynos4_frc_read(struct clocksource *cs) { unsigned int lo, hi; - u32 hi2 = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_U); + u32 hi2 = readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_U); do { hi = hi2; - lo = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_L); - hi2 = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_U); + lo = readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_L); + hi2 = readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_U); } while (hi != hi2); return ((cycle_t)hi << 32) | lo; @@ -202,7 +202,7 @@ static void exynos4_mct_comp0_stop(void) { unsigned int tcon; - tcon = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON); + tcon = readl_relaxed(reg_base + EXYNOS4_MCT_G_TCON); tcon &= ~(MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC); exynos4_mct_write(tcon, EXYNOS4_MCT_G_TCON); @@ -215,7 +215,7 @@ static void exynos4_mct_comp0_start(enum clock_event_mode mode, unsigned int tcon; cycle_t comp_cycle; - tcon = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON); + tcon = readl_relaxed(reg_base + EXYNOS4_MCT_G_TCON); if (mode == CLOCK_EVT_MODE_PERIODIC) { tcon |= MCT_G_TCON_COMP0_AUTO_INC; @@ -306,7 +306,7 @@ static void exynos4_mct_tick_stop(struct mct_clock_event_device *mevt) unsigned long mask = MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START; unsigned long offset = mevt->base + MCT_L_TCON_OFFSET; - tmp = __raw_readl(reg_base + offset); + tmp = readl_relaxed(reg_base + offset); if (tmp & mask) { tmp &= ~mask; exynos4_mct_write(tmp, offset); @@ -328,7 +328,7 @@ static void exynos4_mct_tick_start(unsigned long cycles, /* enable MCT tick interrupt */ exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET); - tmp = __raw_readl(reg_base + mevt->base + MCT_L_TCON_OFFSET); + tmp = readl_relaxed(reg_base + mevt->base + MCT_L_TCON_OFFSET); tmp |= MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START | MCT_L_TCON_INTERVAL_MODE; exynos4_mct_write(tmp, mevt->base + MCT_L_TCON_OFFSET); @@ -380,7 +380,7 @@ static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt) exynos4_mct_tick_stop(mevt); /* Clear the MCT tick interrupt */ - if (__raw_readl(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) { + if (readl_relaxed(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) { exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET); return 1; } else { diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c index 0234c8d2c8f2..95bce5a06078 100644 --- a/drivers/clocksource/samsung_pwm_timer.c +++ b/drivers/clocksource/samsung_pwm_timer.c @@ -118,9 +118,9 @@ static void samsung_time_stop(unsigned int channel) spin_lock_irqsave(&samsung_pwm_lock, flags); - tcon = __raw_readl(pwm.base + REG_TCON); + tcon = readl_relaxed(pwm.base + REG_TCON); tcon &= ~TCON_START(channel); - __raw_writel(tcon, pwm.base + REG_TCON); + writel_relaxed(tcon, pwm.base + REG_TCON); spin_unlock_irqrestore(&samsung_pwm_lock, flags); } @@ -136,14 +136,14 @@ static void samsung_time_setup(unsigned int channel, unsigned long tcnt) spin_lock_irqsave(&samsung_pwm_lock, flags); - tcon = __raw_readl(pwm.base + REG_TCON); + tcon = readl_relaxed(pwm.base + REG_TCON); tcon &= ~(TCON_START(tcon_chan) | TCON_AUTORELOAD(tcon_chan)); tcon |= TCON_MANUALUPDATE(tcon_chan); - __raw_writel(tcnt, pwm.base + REG_TCNTB(channel)); - __raw_writel(tcnt, pwm.base + REG_TCMPB(channel)); - __raw_writel(tcon, pwm.base + REG_TCON); + writel_relaxed(tcnt, pwm.base + REG_TCNTB(channel)); + writel_relaxed(tcnt, pwm.base + REG_TCMPB(channel)); + writel_relaxed(tcon, pwm.base + REG_TCON); spin_unlock_irqrestore(&samsung_pwm_lock, flags); } @@ -158,7 +158,7 @@ static void samsung_time_start(unsigned int channel, bool periodic) spin_lock_irqsave(&samsung_pwm_lock, flags); - tcon = __raw_readl(pwm.base + REG_TCON); + tcon = readl_relaxed(pwm.base + REG_TCON); tcon &= ~TCON_MANUALUPDATE(channel); tcon |= TCON_START(channel); @@ -168,7 +168,7 @@ static void samsung_time_start(unsigned int channel, bool periodic) else tcon &= ~TCON_AUTORELOAD(channel); - __raw_writel(tcon, pwm.base + REG_TCON); + writel_relaxed(tcon, pwm.base + REG_TCON); spin_unlock_irqrestore(&samsung_pwm_lock, flags); } @@ -317,7 +317,7 @@ static u32 notrace samsung_read_sched_clock(void) if (!reg) return 0; - return ~__raw_readl(reg); + return ~readl_relaxed(reg); } static void __init samsung_clocksource_init(void) diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c index add7fbec4fc9..984e560745dd 100644 --- a/drivers/cpufreq/exynos4210-cpufreq.c +++ b/drivers/cpufreq/exynos4210-cpufreq.c @@ -62,20 +62,20 @@ static void exynos4210_set_clkdiv(unsigned int div_index) tmp = apll_freq_4210[div_index].clk_div_cpu0; - __raw_writel(tmp, EXYNOS4_CLKDIV_CPU); + writel_relaxed(tmp, EXYNOS4_CLKDIV_CPU); do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STATCPU); + tmp = readl_relaxed(EXYNOS4_CLKDIV_STATCPU); } while (tmp & 0x1111111); /* Change Divider - CPU1 */ tmp = apll_freq_4210[div_index].clk_div_cpu1; - __raw_writel(tmp, EXYNOS4_CLKDIV_CPU1); + writel_relaxed(tmp, EXYNOS4_CLKDIV_CPU1); do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STATCPU1); + tmp = readl_relaxed(EXYNOS4_CLKDIV_STATCPU1); } while (tmp & 0x11); } @@ -87,30 +87,30 @@ static void exynos4210_set_apll(unsigned int index) clk_set_parent(moutcore, mout_mpll); do { - tmp = (__raw_readl(EXYNOS4_CLKMUX_STATCPU) + tmp = (readl_relaxed(EXYNOS4_CLKMUX_STATCPU) >> EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT); tmp &= 0x7; } while (tmp != 0x2); /* 2. Set APLL Lock time */ - __raw_writel(EXYNOS4_APLL_LOCKTIME, EXYNOS4_APLL_LOCK); + writel_relaxed(EXYNOS4_APLL_LOCKTIME, EXYNOS4_APLL_LOCK); /* 3. Change PLL PMS values */ - tmp = __raw_readl(EXYNOS4_APLL_CON0); + tmp = readl_relaxed(EXYNOS4_APLL_CON0); tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0)); tmp |= apll_freq_4210[index].mps; - __raw_writel(tmp, EXYNOS4_APLL_CON0); + writel_relaxed(tmp, EXYNOS4_APLL_CON0); /* 4. wait_lock_time */ do { - tmp = __raw_readl(EXYNOS4_APLL_CON0); + tmp = readl_relaxed(EXYNOS4_APLL_CON0); } while (!(tmp & (0x1 << EXYNOS4_APLLCON0_LOCKED_SHIFT))); /* 5. MUX_CORE_SEL = APLL */ clk_set_parent(moutcore, mout_apll); do { - tmp = __raw_readl(EXYNOS4_CLKMUX_STATCPU); + tmp = readl_relaxed(EXYNOS4_CLKMUX_STATCPU); tmp &= EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK; } while (tmp != (0x1 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT)); } @@ -134,10 +134,10 @@ static void exynos4210_set_frequency(unsigned int old_index, exynos4210_set_clkdiv(new_index); /* 2. Change just s value in apll m,p,s value */ - tmp = __raw_readl(EXYNOS4_APLL_CON0); + tmp = readl_relaxed(EXYNOS4_APLL_CON0); tmp &= ~(0x7 << 0); tmp |= apll_freq_4210[new_index].mps & 0x7; - __raw_writel(tmp, EXYNOS4_APLL_CON0); + writel_relaxed(tmp, EXYNOS4_APLL_CON0); } else { /* Clock Configuration Procedure */ /* 1. Change the system clock divider values */ @@ -148,10 +148,10 @@ static void exynos4210_set_frequency(unsigned int old_index, } else if (old_index < new_index) { if (!exynos4210_pms_change(old_index, new_index)) { /* 1. Change just s value in apll m,p,s value */ - tmp = __raw_readl(EXYNOS4_APLL_CON0); + tmp = readl_relaxed(EXYNOS4_APLL_CON0); tmp &= ~(0x7 << 0); tmp |= apll_freq_4210[new_index].mps & 0x7; - __raw_writel(tmp, EXYNOS4_APLL_CON0); + writel_relaxed(tmp, EXYNOS4_APLL_CON0); /* 2. Change the system clock divider values */ exynos4210_set_clkdiv(new_index); diff --git a/drivers/cpufreq/exynos4x12-cpufreq.c b/drivers/cpufreq/exynos4x12-cpufreq.c index 08b7477b0aa2..a6ad2b4faa07 100644 --- a/drivers/cpufreq/exynos4x12-cpufreq.c +++ b/drivers/cpufreq/exynos4x12-cpufreq.c @@ -108,21 +108,21 @@ static void exynos4x12_set_clkdiv(unsigned int div_index) tmp = apll_freq_4x12[div_index].clk_div_cpu0; - __raw_writel(tmp, EXYNOS4_CLKDIV_CPU); + writel_relaxed(tmp, EXYNOS4_CLKDIV_CPU); - while (__raw_readl(EXYNOS4_CLKDIV_STATCPU) & 0x11111111) + while (readl_relaxed(EXYNOS4_CLKDIV_STATCPU) & 0x11111111) cpu_relax(); /* Change Divider - CPU1 */ tmp = apll_freq_4x12[div_index].clk_div_cpu1; - __raw_writel(tmp, EXYNOS4_CLKDIV_CPU1); + writel_relaxed(tmp, EXYNOS4_CLKDIV_CPU1); if (soc_is_exynos4212()) stat_cpu1 = 0x11; else stat_cpu1 = 0x111; - while (__raw_readl(EXYNOS4_CLKDIV_STATCPU1) & stat_cpu1) + while (readl_relaxed(EXYNOS4_CLKDIV_STATCPU1) & stat_cpu1) cpu_relax(); } @@ -135,7 +135,7 @@ static void exynos4x12_set_apll(unsigned int index) do { cpu_relax(); - tmp = (__raw_readl(EXYNOS4_CLKMUX_STATCPU) + tmp = (readl_relaxed(EXYNOS4_CLKMUX_STATCPU) >> EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT); tmp &= 0x7; } while (tmp != 0x2); @@ -143,18 +143,18 @@ static void exynos4x12_set_apll(unsigned int index) /* 2. Set APLL Lock time */ pdiv = ((apll_freq_4x12[index].mps >> 8) & 0x3f); - __raw_writel((pdiv * 250), EXYNOS4_APLL_LOCK); + writel_relaxed((pdiv * 250), EXYNOS4_APLL_LOCK); /* 3. Change PLL PMS values */ - tmp = __raw_readl(EXYNOS4_APLL_CON0); + tmp = readl_relaxed(EXYNOS4_APLL_CON0); tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0)); tmp |= apll_freq_4x12[index].mps; - __raw_writel(tmp, EXYNOS4_APLL_CON0); + writel_relaxed(tmp, EXYNOS4_APLL_CON0); /* 4. wait_lock_time */ do { cpu_relax(); - tmp = __raw_readl(EXYNOS4_APLL_CON0); + tmp = readl_relaxed(EXYNOS4_APLL_CON0); } while (!(tmp & (0x1 << EXYNOS4_APLLCON0_LOCKED_SHIFT))); /* 5. MUX_CORE_SEL = APLL */ @@ -162,7 +162,7 @@ static void exynos4x12_set_apll(unsigned int index) do { cpu_relax(); - tmp = __raw_readl(EXYNOS4_CLKMUX_STATCPU); + tmp = readl_relaxed(EXYNOS4_CLKMUX_STATCPU); tmp &= EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK; } while (tmp != (0x1 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT)); } @@ -185,10 +185,10 @@ static void exynos4x12_set_frequency(unsigned int old_index, /* 1. Change the system clock divider values */ exynos4x12_set_clkdiv(new_index); /* 2. Change just s value in apll m,p,s value */ - tmp = __raw_readl(EXYNOS4_APLL_CON0); + tmp = readl_relaxed(EXYNOS4_APLL_CON0); tmp &= ~(0x7 << 0); tmp |= apll_freq_4x12[new_index].mps & 0x7; - __raw_writel(tmp, EXYNOS4_APLL_CON0); + writel_relaxed(tmp, EXYNOS4_APLL_CON0); } else { /* Clock Configuration Procedure */ @@ -200,10 +200,10 @@ static void exynos4x12_set_frequency(unsigned int old_index, } else if (old_index < new_index) { if (!exynos4x12_pms_change(old_index, new_index)) { /* 1. Change just s value in apll m,p,s value */ - tmp = __raw_readl(EXYNOS4_APLL_CON0); + tmp = readl_relaxed(EXYNOS4_APLL_CON0); tmp &= ~(0x7 << 0); tmp |= apll_freq_4x12[new_index].mps & 0x7; - __raw_writel(tmp, EXYNOS4_APLL_CON0); + writel_relaxed(tmp, EXYNOS4_APLL_CON0); /* 2. Change the system clock divider values */ exynos4x12_set_clkdiv(new_index); } else { diff --git a/drivers/cpufreq/exynos5250-cpufreq.c b/drivers/cpufreq/exynos5250-cpufreq.c index 9fae466d7746..127b31ff5d10 100644 --- a/drivers/cpufreq/exynos5250-cpufreq.c +++ b/drivers/cpufreq/exynos5250-cpufreq.c @@ -88,17 +88,17 @@ static void set_clkdiv(unsigned int div_index) tmp = apll_freq_5250[div_index].clk_div_cpu0; - __raw_writel(tmp, EXYNOS5_CLKDIV_CPU0); + writel_relaxed(tmp, EXYNOS5_CLKDIV_CPU0); - while (__raw_readl(EXYNOS5_CLKDIV_STATCPU0) & 0x11111111) + while (readl_relaxed(EXYNOS5_CLKDIV_STATCPU0) & 0x11111111) cpu_relax(); /* Change Divider - CPU1 */ tmp = apll_freq_5250[div_index].clk_div_cpu1; - __raw_writel(tmp, EXYNOS5_CLKDIV_CPU1); + writel_relaxed(tmp, EXYNOS5_CLKDIV_CPU1); - while (__raw_readl(EXYNOS5_CLKDIV_STATCPU1) & 0x11) + while (readl_relaxed(EXYNOS5_CLKDIV_STATCPU1) & 0x11) cpu_relax(); } @@ -112,25 +112,25 @@ static void set_apll(unsigned int new_index, do { cpu_relax(); - tmp = (__raw_readl(EXYNOS5_CLKMUX_STATCPU) >> 16); + tmp = (readl_relaxed(EXYNOS5_CLKMUX_STATCPU) >> 16); tmp &= 0x7; } while (tmp != 0x2); /* 2. Set APLL Lock time */ pdiv = ((apll_freq_5250[new_index].mps >> 8) & 0x3f); - __raw_writel((pdiv * 250), EXYNOS5_APLL_LOCK); + writel_relaxed((pdiv * 250), EXYNOS5_APLL_LOCK); /* 3. Change PLL PMS values */ - tmp = __raw_readl(EXYNOS5_APLL_CON0); + tmp = readl_relaxed(EXYNOS5_APLL_CON0); tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0)); tmp |= apll_freq_5250[new_index].mps; - __raw_writel(tmp, EXYNOS5_APLL_CON0); + writel_relaxed(tmp, EXYNOS5_APLL_CON0); /* 4. wait_lock_time */ do { cpu_relax(); - tmp = __raw_readl(EXYNOS5_APLL_CON0); + tmp = readl_relaxed(EXYNOS5_APLL_CON0); } while (!(tmp & (0x1 << 29))); /* 5. MUX_CORE_SEL = APLL */ @@ -138,7 +138,7 @@ static void set_apll(unsigned int new_index, do { cpu_relax(); - tmp = __raw_readl(EXYNOS5_CLKMUX_STATCPU); + tmp = readl_relaxed(EXYNOS5_CLKMUX_STATCPU); tmp &= (0x7 << 16); } while (tmp != (0x1 << 16)); @@ -162,10 +162,10 @@ static void exynos5250_set_frequency(unsigned int old_index, /* 1. Change the system clock divider values */ set_clkdiv(new_index); /* 2. Change just s value in apll m,p,s value */ - tmp = __raw_readl(EXYNOS5_APLL_CON0); + tmp = readl_relaxed(EXYNOS5_APLL_CON0); tmp &= ~(0x7 << 0); tmp |= apll_freq_5250[new_index].mps & 0x7; - __raw_writel(tmp, EXYNOS5_APLL_CON0); + writel_relaxed(tmp, EXYNOS5_APLL_CON0); } else { /* Clock Configuration Procedure */ @@ -177,10 +177,10 @@ static void exynos5250_set_frequency(unsigned int old_index, } else if (old_index < new_index) { if (!exynos5250_pms_change(old_index, new_index)) { /* 1. Change just s value in apll m,p,s value */ - tmp = __raw_readl(EXYNOS5_APLL_CON0); + tmp = readl_relaxed(EXYNOS5_APLL_CON0); tmp &= ~(0x7 << 0); tmp |= apll_freq_5250[new_index].mps & 0x7; - __raw_writel(tmp, EXYNOS5_APLL_CON0); + writel_relaxed(tmp, EXYNOS5_APLL_CON0); /* 2. Change the system clock divider values */ set_clkdiv(new_index); } else { diff --git a/drivers/cpufreq/exynos5440-cpufreq.c b/drivers/cpufreq/exynos5440-cpufreq.c index 0c74018eda47..30a97b5a49b9 100644 --- a/drivers/cpufreq/exynos5440-cpufreq.c +++ b/drivers/cpufreq/exynos5440-cpufreq.c @@ -158,7 +158,7 @@ static int init_div_table(void) tmp = (clk_div | ema_div | (volt_id << P0_7_VDD_SHIFT) | ((freq / FREQ_UNIT) << P0_7_FREQ_SHIFT)); - __raw_writel(tmp, dvfs_info->base + XMU_PMU_P0_7 + 4 * i); + writel_relaxed(tmp, dvfs_info->base + XMU_PMU_P0_7 + 4 * i); } rcu_read_unlock(); @@ -170,17 +170,17 @@ static void exynos_enable_dvfs(void) unsigned int tmp, i, cpu; struct cpufreq_frequency_table *freq_table = dvfs_info->freq_table; /* Disable DVFS */ - __raw_writel(0, dvfs_info->base + XMU_DVFS_CTRL); + writel_relaxed(0, dvfs_info->base + XMU_DVFS_CTRL); /* Enable PSTATE Change Event */ - tmp = __raw_readl(dvfs_info->base + XMU_PMUEVTEN); + tmp = readl_relaxed(dvfs_info->base + XMU_PMUEVTEN); tmp |= (1 << PSTATE_CHANGED_EVTEN_SHIFT); - __raw_writel(tmp, dvfs_info->base + XMU_PMUEVTEN); + writel_relaxed(tmp, dvfs_info->base + XMU_PMUEVTEN); /* Enable PSTATE Change IRQ */ - tmp = __raw_readl(dvfs_info->base + XMU_PMUIRQEN); + tmp = readl_relaxed(dvfs_info->base + XMU_PMUIRQEN); tmp |= (1 << PSTATE_CHANGED_IRQEN_SHIFT); - __raw_writel(tmp, dvfs_info->base + XMU_PMUIRQEN); + writel_relaxed(tmp, dvfs_info->base + XMU_PMUIRQEN); /* Set initial performance index */ for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) @@ -198,14 +198,14 @@ static void exynos_enable_dvfs(void) dvfs_info->cur_frequency); for (cpu = 0; cpu < CONFIG_NR_CPUS; cpu++) { - tmp = __raw_readl(dvfs_info->base + XMU_C0_3_PSTATE + cpu * 4); + tmp = readl_relaxed(dvfs_info->base + XMU_C0_3_PSTATE + cpu * 4); tmp &= ~(P_VALUE_MASK << C0_3_PSTATE_NEW_SHIFT); tmp |= (i << C0_3_PSTATE_NEW_SHIFT); - __raw_writel(tmp, dvfs_info->base + XMU_C0_3_PSTATE + cpu * 4); + writel_relaxed(tmp, dvfs_info->base + XMU_C0_3_PSTATE + cpu * 4); } /* Enable DVFS */ - __raw_writel(1 << XMU_DVFS_CTRL_EN_SHIFT, + writel_relaxed(1 << XMU_DVFS_CTRL_EN_SHIFT, dvfs_info->base + XMU_DVFS_CTRL); } @@ -242,11 +242,11 @@ static int exynos_target(struct cpufreq_policy *policy, /* Set the target frequency in all C0_3_PSTATE register */ for_each_cpu(i, policy->cpus) { - tmp = __raw_readl(dvfs_info->base + XMU_C0_3_PSTATE + i * 4); + tmp = readl_relaxed(dvfs_info->base + XMU_C0_3_PSTATE + i * 4); tmp &= ~(P_VALUE_MASK << C0_3_PSTATE_NEW_SHIFT); tmp |= (index << C0_3_PSTATE_NEW_SHIFT); - __raw_writel(tmp, dvfs_info->base + XMU_C0_3_PSTATE + i * 4); + writel_relaxed(tmp, dvfs_info->base + XMU_C0_3_PSTATE + i * 4); } out: mutex_unlock(&cpufreq_lock); @@ -266,7 +266,7 @@ static void exynos_cpufreq_work(struct work_struct *work) mutex_lock(&cpufreq_lock); freqs.old = dvfs_info->cur_frequency; - cur_pstate = __raw_readl(dvfs_info->base + XMU_P_STATUS); + cur_pstate = readl_relaxed(dvfs_info->base + XMU_P_STATUS); if (cur_pstate >> C0_3_PSTATE_VALID_SHIFT & 0x1) index = (cur_pstate >> C0_3_PSTATE_CURR_SHIFT) & P_VALUE_MASK; else @@ -291,9 +291,9 @@ static irqreturn_t exynos_cpufreq_irq(int irq, void *id) { unsigned int tmp; - tmp = __raw_readl(dvfs_info->base + XMU_PMUIRQ); + tmp = readl_relaxed(dvfs_info->base + XMU_PMUIRQ); if (tmp >> PSTATE_CHANGED_SHIFT & 0x1) { - __raw_writel(tmp, dvfs_info->base + XMU_PMUIRQ); + writel_relaxed(tmp, dvfs_info->base + XMU_PMUIRQ); disable_irq_nosync(irq); schedule_work(&dvfs_info->irq_work); } diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index b22ca7933745..144d18003538 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -47,10 +47,10 @@ int samsung_gpio_setpull_updown(struct samsung_gpio_chip *chip, int shift = off * 2; u32 pup; - pup = __raw_readl(reg); + pup = readl_relaxed(reg); pup &= ~(3 << shift); pup |= pull << shift; - __raw_writel(pup, reg); + writel_relaxed(pup, reg); return 0; } @@ -60,7 +60,7 @@ samsung_gpio_pull_t samsung_gpio_getpull_updown(struct samsung_gpio_chip *chip, { void __iomem *reg = chip->base + 0x08; int shift = off * 2; - u32 pup = __raw_readl(reg); + u32 pup = readl_relaxed(reg); pup >>= shift; pup &= 0x3; @@ -113,7 +113,7 @@ static int s3c24xx_gpio_setpull_1(struct samsung_gpio_chip *chip, samsung_gpio_pull_t updown) { void __iomem *reg = chip->base + 0x08; - u32 pup = __raw_readl(reg); + u32 pup = readl_relaxed(reg); if (pull == updown) pup &= ~(1 << off); @@ -122,7 +122,7 @@ static int s3c24xx_gpio_setpull_1(struct samsung_gpio_chip *chip, else return -EINVAL; - __raw_writel(pup, reg); + writel_relaxed(pup, reg); return 0; } @@ -131,7 +131,7 @@ static samsung_gpio_pull_t s3c24xx_gpio_getpull_1(struct samsung_gpio_chip *chip samsung_gpio_pull_t updown) { void __iomem *reg = chip->base + 0x08; - u32 pup = __raw_readl(reg); + u32 pup = readl_relaxed(reg); pup &= (1 << off); return pup ? S3C_GPIO_PULL_NONE : updown; @@ -212,10 +212,10 @@ static int samsung_gpio_setcfg_2bit(struct samsung_gpio_chip *chip, cfg <<= shift; } - con = __raw_readl(reg); + con = readl_relaxed(reg); con &= ~(0x3 << shift); con |= cfg; - __raw_writel(con, reg); + writel_relaxed(con, reg); return 0; } @@ -235,7 +235,7 @@ static unsigned int samsung_gpio_getcfg_2bit(struct samsung_gpio_chip *chip, { u32 con; - con = __raw_readl(chip->base); + con = readl_relaxed(chip->base); con >>= off * 2; con &= 3; @@ -275,10 +275,10 @@ static int samsung_gpio_setcfg_4bit(struct samsung_gpio_chip *chip, cfg <<= shift; } - con = __raw_readl(reg); + con = readl_relaxed(reg); con &= ~(0xf << shift); con |= cfg; - __raw_writel(con, reg); + writel_relaxed(con, reg); return 0; } @@ -305,7 +305,7 @@ static unsigned samsung_gpio_getcfg_4bit(struct samsung_gpio_chip *chip, if (off < 8 && chip->chip.ngpio > 8) reg -= 4; - con = __raw_readl(reg); + con = readl_relaxed(reg); con >>= shift; con &= 0xf; @@ -343,10 +343,10 @@ static int s3c24xx_gpio_setcfg_abank(struct samsung_gpio_chip *chip, cfg <<= shift; } - con = __raw_readl(reg); + con = readl_relaxed(reg); con &= ~(0x1 << shift); con |= cfg; - __raw_writel(con, reg); + writel_relaxed(con, reg); return 0; } @@ -368,7 +368,7 @@ static unsigned s3c24xx_gpio_getcfg_abank(struct samsung_gpio_chip *chip, { u32 con; - con = __raw_readl(chip->base); + con = readl_relaxed(chip->base); con >>= off; con &= 1; con++; @@ -408,10 +408,10 @@ static int s5p64x0_gpio_setcfg_rbank(struct samsung_gpio_chip *chip, cfg <<= shift; } - con = __raw_readl(reg); + con = readl_relaxed(reg); con &= ~(0xf << shift); con |= cfg; - __raw_writel(con, reg); + writel_relaxed(con, reg); return 0; } @@ -527,10 +527,10 @@ static int samsung_gpiolib_2bit_input(struct gpio_chip *chip, unsigned offset) samsung_gpio_lock(ourchip, flags); - con = __raw_readl(base + 0x00); + con = readl_relaxed(base + 0x00); con &= ~(3 << (offset * 2)); - __raw_writel(con, base + 0x00); + writel_relaxed(con, base + 0x00); samsung_gpio_unlock(ourchip, flags); return 0; @@ -547,18 +547,18 @@ static int samsung_gpiolib_2bit_output(struct gpio_chip *chip, samsung_gpio_lock(ourchip, flags); - dat = __raw_readl(base + 0x04); + dat = readl_relaxed(base + 0x04); dat &= ~(1 << offset); if (value) dat |= 1 << offset; - __raw_writel(dat, base + 0x04); + writel_relaxed(dat, base + 0x04); - con = __raw_readl(base + 0x00); + con = readl_relaxed(base + 0x00); con &= ~(3 << (offset * 2)); con |= 1 << (offset * 2); - __raw_writel(con, base + 0x00); - __raw_writel(dat, base + 0x04); + writel_relaxed(con, base + 0x00); + writel_relaxed(dat, base + 0x04); samsung_gpio_unlock(ourchip, flags); return 0; @@ -587,12 +587,12 @@ static int samsung_gpiolib_4bit_input(struct gpio_chip *chip, void __iomem *base = ourchip->base; unsigned long con; - con = __raw_readl(base + GPIOCON_OFF); + con = readl_relaxed(base + GPIOCON_OFF); if (ourchip->bitmap_gpio_int & BIT(offset)) con |= 0xf << con_4bit_shift(offset); else con &= ~(0xf << con_4bit_shift(offset)); - __raw_writel(con, base + GPIOCON_OFF); + writel_relaxed(con, base + GPIOCON_OFF); pr_debug("%s: %p: CON now %08lx\n", __func__, base, con); @@ -607,20 +607,20 @@ static int samsung_gpiolib_4bit_output(struct gpio_chip *chip, unsigned long con; unsigned long dat; - con = __raw_readl(base + GPIOCON_OFF); + con = readl_relaxed(base + GPIOCON_OFF); con &= ~(0xf << con_4bit_shift(offset)); con |= 0x1 << con_4bit_shift(offset); - dat = __raw_readl(base + GPIODAT_OFF); + dat = readl_relaxed(base + GPIODAT_OFF); if (value) dat |= 1 << offset; else dat &= ~(1 << offset); - __raw_writel(dat, base + GPIODAT_OFF); - __raw_writel(con, base + GPIOCON_OFF); - __raw_writel(dat, base + GPIODAT_OFF); + writel_relaxed(dat, base + GPIODAT_OFF); + writel_relaxed(con, base + GPIOCON_OFF); + writel_relaxed(dat, base + GPIODAT_OFF); pr_debug("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); @@ -662,9 +662,9 @@ static int samsung_gpiolib_4bit2_input(struct gpio_chip *chip, else regcon -= 4; - con = __raw_readl(regcon); + con = readl_relaxed(regcon); con &= ~(0xf << con_4bit_shift(offset)); - __raw_writel(con, regcon); + writel_relaxed(con, regcon); pr_debug("%s: %p: CON %08lx\n", __func__, base, con); @@ -686,20 +686,20 @@ static int samsung_gpiolib_4bit2_output(struct gpio_chip *chip, else regcon -= 4; - con = __raw_readl(regcon); + con = readl_relaxed(regcon); con &= ~(0xf << con_4bit_shift(con_offset)); con |= 0x1 << con_4bit_shift(con_offset); - dat = __raw_readl(base + GPIODAT_OFF); + dat = readl_relaxed(base + GPIODAT_OFF); if (value) dat |= 1 << offset; else dat &= ~(1 << offset); - __raw_writel(dat, base + GPIODAT_OFF); - __raw_writel(con, regcon); - __raw_writel(dat, base + GPIODAT_OFF); + writel_relaxed(dat, base + GPIODAT_OFF); + writel_relaxed(con, regcon); + writel_relaxed(dat, base + GPIODAT_OFF); pr_debug("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); @@ -725,19 +725,19 @@ static int s3c24xx_gpiolib_banka_output(struct gpio_chip *chip, local_irq_save(flags); - con = __raw_readl(base + 0x00); - dat = __raw_readl(base + 0x04); + con = readl_relaxed(base + 0x00); + dat = readl_relaxed(base + 0x04); dat &= ~(1 << offset); if (value) dat |= 1 << offset; - __raw_writel(dat, base + 0x04); + writel_relaxed(dat, base + 0x04); con &= ~(1 << offset); - __raw_writel(con, base + 0x00); - __raw_writel(dat, base + 0x04); + writel_relaxed(con, base + 0x00); + writel_relaxed(dat, base + 0x04); local_irq_restore(flags); return 0; @@ -773,9 +773,9 @@ static int s5p64x0_gpiolib_rbank_input(struct gpio_chip *chip, samsung_gpio_lock(ourchip, flags); - con = __raw_readl(regcon); + con = readl_relaxed(regcon); con &= ~(0xf << con_4bit_shift(offset)); - __raw_writel(con, regcon); + writel_relaxed(con, regcon); samsung_gpio_unlock(ourchip, flags); @@ -811,18 +811,18 @@ static int s5p64x0_gpiolib_rbank_output(struct gpio_chip *chip, samsung_gpio_lock(ourchip, flags); - con = __raw_readl(regcon); + con = readl_relaxed(regcon); con &= ~(0xf << con_4bit_shift(con_offset)); con |= 0x1 << con_4bit_shift(con_offset); - dat = __raw_readl(base + GPIODAT_OFF); + dat = readl_relaxed(base + GPIODAT_OFF); if (value) dat |= 1 << offset; else dat &= ~(1 << offset); - __raw_writel(con, regcon); - __raw_writel(dat, base + GPIODAT_OFF); + writel_relaxed(con, regcon); + writel_relaxed(dat, base + GPIODAT_OFF); samsung_gpio_unlock(ourchip, flags); @@ -839,11 +839,11 @@ static void samsung_gpiolib_set(struct gpio_chip *chip, samsung_gpio_lock(ourchip, flags); - dat = __raw_readl(base + 0x04); + dat = readl_relaxed(base + 0x04); dat &= ~(1 << offset); if (value) dat |= 1 << offset; - __raw_writel(dat, base + 0x04); + writel_relaxed(dat, base + 0x04); samsung_gpio_unlock(ourchip, flags); } @@ -853,7 +853,7 @@ static int samsung_gpiolib_get(struct gpio_chip *chip, unsigned offset) struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); unsigned long val; - val = __raw_readl(ourchip->base + 0x04); + val = readl_relaxed(ourchip->base + 0x04); val >>= offset; val &= 1; @@ -3236,7 +3236,7 @@ s5p_gpio_drvstr_t s5p_gpio_get_drvstr(unsigned int pin) shift = off * 2; reg = chip->base + 0x0C; - drvstr = __raw_readl(reg); + drvstr = readl_relaxed(reg); drvstr = drvstr >> shift; drvstr &= 0x3; @@ -3259,11 +3259,11 @@ int s5p_gpio_set_drvstr(unsigned int pin, s5p_gpio_drvstr_t drvstr) shift = off * 2; reg = chip->base + 0x0C; - tmp = __raw_readl(reg); + tmp = readl_relaxed(reg); tmp &= ~(0x3 << shift); tmp |= drvstr << shift; - __raw_writel(tmp, reg); + writel_relaxed(tmp, reg); return 0; } @@ -3277,10 +3277,10 @@ unsigned int s3c2410_modify_misccr(unsigned int clear, unsigned int change) unsigned long misccr; local_irq_save(flags); - misccr = __raw_readl(S3C24XX_MISCCR); + misccr = readl_relaxed(S3C24XX_MISCCR); misccr &= ~clear; misccr ^= change; - __raw_writel(misccr, S3C24XX_MISCCR); + writel_relaxed(misccr, S3C24XX_MISCCR); local_irq_restore(flags); return misccr; diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index cab1c91b75a3..733f6983d922 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -86,6 +86,7 @@ #define QUIRK_S3C2440 (1 << 0) #define QUIRK_HDMIPHY (1 << 1) #define QUIRK_NO_GPIO (1 << 2) +#define QUIRK_SATAPHY (1 << 3) /* Max time to wait for bus to become idle after a xfer (in us) */ #define S3C2410_IDLE_TIMEOUT 5000 @@ -142,10 +143,14 @@ static struct platform_device_id s3c24xx_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); +static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat); + #ifdef CONFIG_OF static const struct of_device_id s3c24xx_i2c_match[] = { { .compatible = "samsung,s3c2410-i2c", .data = (void *)0 }, { .compatible = "samsung,s3c2440-i2c", .data = (void *)QUIRK_S3C2440 }, + { .compatible = "samsung,exynos5-sata-phy-i2c", + .data = (void *)(QUIRK_S3C2440 | QUIRK_SATAPHY | QUIRK_NO_GPIO) }, { .compatible = "samsung,s3c2440-hdmiphy-i2c", .data = (void *)(QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO) }, { .compatible = "samsung,exynos5440-i2c", @@ -188,7 +193,8 @@ static inline void s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret) if (ret) i2c->msg_idx = ret; - wake_up(&i2c->wait); + if (!(i2c->quirks & QUIRK_SATAPHY)) + wake_up(&i2c->wait); } static inline void s3c24xx_i2c_disable_ack(struct s3c24xx_i2c *i2c) @@ -226,6 +232,23 @@ static inline void s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c) } +static bool is_ack(struct s3c24xx_i2c *i2c) +{ + u32 time_out = i2c->tx_setup; + + while (--time_out) { + if (readl(i2c->regs + S3C2410_IICCON) + & S3C2410_IICCON_IRQPEND) { + if (!(readl(i2c->regs + S3C2410_IICSTAT) + & S3C2410_IICSTAT_LASTBIT)) + return true; + } + udelay(time_out); + } + + return false; +} + /* s3c24xx_i2c_message_start * * put the start of a message onto the bus @@ -269,6 +292,15 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, stat |= S3C2410_IICSTAT_START; writel(stat, i2c->regs + S3C2410_IICSTAT); + + if (i2c->quirks & QUIRK_SATAPHY) { + while ((i2c->msg_num != 0) && is_ack(i2c)) { + i2c_s3c_irq_nextbyte(i2c, stat); + stat = readl(i2c->regs + S3C2410_IICSTAT); + if (stat & S3C2410_IICSTAT_ARBITR) + dev_err(i2c->dev, "deal with arbitration loss\n"); + } + } } static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret) @@ -676,21 +708,27 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, s3c24xx_i2c_enable_irq(i2c); s3c24xx_i2c_message_start(i2c, msgs); - timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); + if (!(i2c->quirks & QUIRK_SATAPHY)) { + + timeout = wait_event_timeout(i2c->wait, + i2c->msg_num == 0, HZ * 5); - ret = i2c->msg_idx; + ret = i2c->msg_idx; - /* having these next two as dev_err() makes life very - * noisy when doing an i2cdetect */ + /* having these next two as dev_err() makes life very + * noisy when doing an i2cdetect */ - if (timeout == 0) - dev_dbg(i2c->dev, "timeout\n"); - else if (ret != num) - dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret); + if (timeout == 0) + dev_dbg(i2c->dev, "timeout\n"); + else if (ret != num) + dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret); + } /* For QUIRK_HDMIPHY, bus is already disabled */ - if (i2c->quirks & QUIRK_HDMIPHY) + if (i2c->quirks & QUIRK_HDMIPHY) { + pr_warn("skipping i2c wait idle due to HDMIPHY stuff\n"); goto out; + } s3c24xx_i2c_wait_idle(i2c); @@ -821,6 +859,9 @@ static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got) if (div1 == 512) iiccon |= S3C2410_IICCON_TXDIV_512; + if (i2c->quirks & QUIRK_SATAPHY) + iiccon |= S3C2410_IICCON_SCALE(2); + writel(iiccon, i2c->regs + S3C2410_IICCON); if (i2c->quirks & QUIRK_S3C2440) { @@ -1118,18 +1159,20 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) * ensure no current IRQs pending */ - i2c->irq = ret = platform_get_irq(pdev, 0); - if (ret <= 0) { - dev_err(&pdev->dev, "cannot find IRQ\n"); - return ret; - } + if (!(i2c->quirks & QUIRK_SATAPHY)) { + i2c->irq = ret = platform_get_irq(pdev, 0); + if (ret <= 0) { + dev_err(&pdev->dev, "cannot find IRQ\n"); + return ret; + } - ret = devm_request_irq(&pdev->dev, i2c->irq, s3c24xx_i2c_irq, 0, - dev_name(&pdev->dev), i2c); + ret = devm_request_irq(&pdev->dev, i2c->irq, s3c24xx_i2c_irq, 0, + dev_name(&pdev->dev), i2c); - if (ret != 0) { - dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); - return ret; + if (ret != 0) { + dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); + return ret; + } } ret = s3c24xx_i2c_register_cpufreq(i2c); diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c index a9d2b2fa4afd..e7cf548d1ea1 100644 --- a/drivers/irqchip/exynos-combiner.c +++ b/drivers/irqchip/exynos-combiner.c @@ -54,14 +54,14 @@ static void combiner_mask_irq(struct irq_data *data) { u32 mask = 1 << (data->hwirq % 32); - __raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_CLEAR); + writel_relaxed(mask, combiner_base(data) + COMBINER_ENABLE_CLEAR); } static void combiner_unmask_irq(struct irq_data *data) { u32 mask = 1 << (data->hwirq % 32); - __raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_SET); + writel_relaxed(mask, combiner_base(data) + COMBINER_ENABLE_SET); } static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) @@ -74,7 +74,7 @@ static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) chained_irq_enter(chip, desc); spin_lock(&irq_controller_lock); - status = __raw_readl(chip_data->base + COMBINER_INT_STATUS); + status = readl_relaxed(chip_data->base + COMBINER_INT_STATUS); spin_unlock(&irq_controller_lock); status &= chip_data->irq_mask; @@ -135,7 +135,7 @@ static void __init combiner_init_one(struct combiner_chip_data *combiner_data, combiner_data->parent_irq = irq; /* Disable all interrupts */ - __raw_writel(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR); + writel_relaxed(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR); } #ifdef CONFIG_OF diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index bc3a1bc4940f..9bc247cf89ee 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1214,7 +1214,7 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt) buf += len; cnt -= len; if (host->part_buf_count == 2) { - mci_writew(host, DATA(host->data_offset), + mci_writew_data(host, DATA(host->data_offset), host->part_buf16); host->part_buf_count = 0; } @@ -1232,7 +1232,7 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt) cnt -= len; /* push data from aligned buffer into fifo */ for (i = 0; i < items; ++i) - mci_writew(host, DATA(host->data_offset), + mci_writew_data(host, DATA(host->data_offset), aligned_buf[i]); } } else @@ -1240,7 +1240,7 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt) { u16 *pdata = buf; for (; cnt >= 2; cnt -= 2) - mci_writew(host, DATA(host->data_offset), *pdata++); + mci_writew_data(host, DATA(host->data_offset), *pdata++); buf = pdata; } /* put anything remaining in the part_buf */ @@ -1249,7 +1249,7 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt) /* Push data if we have reached the expected data length */ if ((data->bytes_xfered + init_cnt) == (data->blksz * data->blocks)) - mci_writew(host, DATA(host->data_offset), + mci_writew_data(host, DATA(host->data_offset), host->part_buf16); } } @@ -1265,7 +1265,7 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt) int items = len >> 1; int i; for (i = 0; i < items; ++i) - aligned_buf[i] = mci_readw(host, + aligned_buf[i] = mci_readw_data(host, DATA(host->data_offset)); /* memcpy from aligned buffer into output buffer */ memcpy(buf, aligned_buf, len); @@ -1277,11 +1277,11 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt) { u16 *pdata = buf; for (; cnt >= 2; cnt -= 2) - *pdata++ = mci_readw(host, DATA(host->data_offset)); + *pdata++ = mci_readw_data(host, DATA(host->data_offset)); buf = pdata; } if (cnt) { - host->part_buf16 = mci_readw(host, DATA(host->data_offset)); + host->part_buf16 = mci_readw_data(host, DATA(host->data_offset)); dw_mci_pull_final_bytes(host, buf, cnt); } } @@ -1297,7 +1297,7 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt) buf += len; cnt -= len; if (host->part_buf_count == 4) { - mci_writel(host, DATA(host->data_offset), + mci_writel_data(host, DATA(host->data_offset), host->part_buf32); host->part_buf_count = 0; } @@ -1315,7 +1315,7 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt) cnt -= len; /* push data from aligned buffer into fifo */ for (i = 0; i < items; ++i) - mci_writel(host, DATA(host->data_offset), + mci_writel_data(host, DATA(host->data_offset), aligned_buf[i]); } } else @@ -1323,7 +1323,7 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt) { u32 *pdata = buf; for (; cnt >= 4; cnt -= 4) - mci_writel(host, DATA(host->data_offset), *pdata++); + mci_writel_data(host, DATA(host->data_offset), *pdata++); buf = pdata; } /* put anything remaining in the part_buf */ @@ -1332,7 +1332,7 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt) /* Push data if we have reached the expected data length */ if ((data->bytes_xfered + init_cnt) == (data->blksz * data->blocks)) - mci_writel(host, DATA(host->data_offset), + mci_writel_data(host, DATA(host->data_offset), host->part_buf32); } } @@ -1348,7 +1348,7 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt) int items = len >> 2; int i; for (i = 0; i < items; ++i) - aligned_buf[i] = mci_readl(host, + aligned_buf[i] = mci_readl_data(host, DATA(host->data_offset)); /* memcpy from aligned buffer into output buffer */ memcpy(buf, aligned_buf, len); @@ -1360,11 +1360,11 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt) { u32 *pdata = buf; for (; cnt >= 4; cnt -= 4) - *pdata++ = mci_readl(host, DATA(host->data_offset)); + *pdata++ = mci_readl_data(host, DATA(host->data_offset)); buf = pdata; } if (cnt) { - host->part_buf32 = mci_readl(host, DATA(host->data_offset)); + host->part_buf32 = mci_readl_data(host, DATA(host->data_offset)); dw_mci_pull_final_bytes(host, buf, cnt); } } @@ -1381,7 +1381,7 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt) cnt -= len; if (host->part_buf_count == 8) { - mci_writeq(host, DATA(host->data_offset), + mci_writeq_data(host, DATA(host->data_offset), host->part_buf); host->part_buf_count = 0; } @@ -1399,7 +1399,7 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt) cnt -= len; /* push data from aligned buffer into fifo */ for (i = 0; i < items; ++i) - mci_writeq(host, DATA(host->data_offset), + mci_writeq_data(host, DATA(host->data_offset), aligned_buf[i]); } } else @@ -1407,7 +1407,7 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt) { u64 *pdata = buf; for (; cnt >= 8; cnt -= 8) - mci_writeq(host, DATA(host->data_offset), *pdata++); + mci_writeq_data(host, DATA(host->data_offset), *pdata++); buf = pdata; } /* put anything remaining in the part_buf */ @@ -1416,7 +1416,7 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt) /* Push data if we have reached the expected data length */ if ((data->bytes_xfered + init_cnt) == (data->blksz * data->blocks)) - mci_writeq(host, DATA(host->data_offset), + mci_writeq_data(host, DATA(host->data_offset), host->part_buf); } } @@ -1432,7 +1432,7 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt) int items = len >> 3; int i; for (i = 0; i < items; ++i) - aligned_buf[i] = mci_readq(host, + aligned_buf[i] = mci_readq_data(host, DATA(host->data_offset)); /* memcpy from aligned buffer into output buffer */ memcpy(buf, aligned_buf, len); @@ -1444,11 +1444,11 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt) { u64 *pdata = buf; for (; cnt >= 8; cnt -= 8) - *pdata++ = mci_readq(host, DATA(host->data_offset)); + *pdata++ = mci_readq_data(host, DATA(host->data_offset)); buf = pdata; } if (cnt) { - host->part_buf = mci_readq(host, DATA(host->data_offset)); + host->part_buf = mci_readq_data(host, DATA(host->data_offset)); dw_mci_pull_final_bytes(host, buf, cnt); } } diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 0b74189e7ee7..37591fc4f5b2 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -144,21 +144,27 @@ /* Register access macros */ #define mci_readl(dev, reg) \ - __raw_readl((dev)->regs + SDMMC_##reg) + readl_relaxed((dev)->regs + SDMMC_##reg) #define mci_writel(dev, reg, value) \ - __raw_writel((value), (dev)->regs + SDMMC_##reg) + writel_relaxed((value), (dev)->regs + SDMMC_##reg) /* 16-bit FIFO access macros */ -#define mci_readw(dev, reg) \ +#define mci_readw_data(dev, reg) \ __raw_readw((dev)->regs + SDMMC_##reg) -#define mci_writew(dev, reg, value) \ +#define mci_writew_data(dev, reg, value) \ __raw_writew((value), (dev)->regs + SDMMC_##reg) +/* 32-bit FIFO access macros */ +#define mci_readl_data(dev, reg) \ + __raw_readl((dev)->regs + SDMMC_##reg) +#define mci_writel_data(dev, reg, value) \ + __raw_writel((value), (dev)->regs + SDMMC_##reg) + /* 64-bit FIFO access macros */ #ifdef readq -#define mci_readq(dev, reg) \ +#define mci_readq_data(dev, reg) \ __raw_readq((dev)->regs + SDMMC_##reg) -#define mci_writeq(dev, reg, value) \ +#define mci_writeq_data(dev, reg, value) \ __raw_writeq((value), (dev)->regs + SDMMC_##reg) #else /* @@ -169,10 +175,10 @@ * executed on those machines. Defining these macros here keeps the * rest of the code free from ifdefs. */ -#define mci_readq(dev, reg) \ - (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg)) -#define mci_writeq(dev, reg, value) \ - (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg) = (value)) +#define mci_readq_data(dev, reg) \ + ({ u64 __r = le64_to_cpu(*(volatile u64 __force *)((dev)->regs + SDMMC_##reg)); __r; }) +#define mci_writeq_data(dev, reg, value) \ + (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg) = (cpu_to_le64(value))) #endif extern int dw_mci_probe(struct dw_mci *host); diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index 97b5de7aebdb..fd10fae81dc8 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -44,6 +44,35 @@ struct ax88172_int_data { __le16 res3; } __packed; +static char asix_mac_addr[6]; +static int __init asix_setup_mac(char *macstr) +{ + int i, h, l; + + if (!macstr) + return 0; + + for (i = 0; i < 6; i++) { + if (i != 5 && *(macstr + 2) != ':') + return 0; + + h = hex_to_bin(*macstr++); + if (h == -1) + return 0; + + l = hex_to_bin(*macstr++); + if (l == -1) + return 0; + + macstr++; + asix_mac_addr[i] = (h << 4) + l; + } + + return 0; +} + +__setup("mac=", asix_setup_mac); + static void asix_status(struct usbnet *dev, struct urb *urb) { struct ax88172_int_data *event; @@ -62,6 +91,9 @@ static void asix_status(struct usbnet *dev, struct urb *urb) static void asix_set_netdev_dev_addr(struct usbnet *dev, u8 *addr) { + if (!is_valid_ether_addr(addr)) + memcpy(addr, asix_mac_addr, ETH_ALEN); + if (is_valid_ether_addr(addr)) { memcpy(dev->net->dev_addr, addr, ETH_ALEN); } else { diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c index 788b1ddcac6c..e1de99dbd23b 100644 --- a/drivers/thermal/exynos_thermal.c +++ b/drivers/thermal/exynos_thermal.c @@ -590,7 +590,7 @@ static int exynos_tmu_initialize(struct platform_device *pdev) } if (data->soc == SOC_ARCH_EXYNOS) { - __raw_writel(EXYNOS_TRIMINFO_RELOAD, + writel_relaxed(EXYNOS_TRIMINFO_RELOAD, data->base + EXYNOS_TMU_TRIMINFO_CON); } /* Save trimming info in order to perform calibration */ diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 13471dd95793..d983370eb05b 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -231,7 +231,9 @@ static void altera_uart_rx_chars(struct altera_uart *pp) flag); } + spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); + spin_lock(&port->lock); } static void altera_uart_tx_chars(struct altera_uart *pp) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 0c8a9fa2be6c..9bd5eb2924db 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -59,6 +59,41 @@ #define S3C24XX_SERIAL_MAJOR 204 #define S3C24XX_SERIAL_MINOR 64 +#ifndef CONFIG_CPU_BIG_ENDIAN /* little endian */ +static inline void __hw_set_bit(int nr, volatile unsigned long *addr) +{ + __set_bit(nr, addr); +} + +static inline void __hw_clear_bit(int nr, volatile unsigned long *addr) +{ + __clear_bit(nr, addr); +} +#else +static inline void __hw_set_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); + unsigned long val = le32_to_cpu(*p); + + val |= mask; + + *p = cpu_to_le32(val); +} + +static inline void __hw_clear_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); + + unsigned long val = le32_to_cpu(*p); + + val &= ~mask; + + *p = cpu_to_le32(val); +} +#endif + /* macros to change one thing to another */ #define tx_enabled(port) ((port)->unused[0]) @@ -138,7 +173,7 @@ static void s3c24xx_serial_stop_tx(struct uart_port *port) if (tx_enabled(port)) { if (s3c24xx_serial_has_interrupt_mask(port)) - __set_bit(S3C64XX_UINTM_TXD, + __hw_set_bit(S3C64XX_UINTM_TXD, portaddrl(port, S3C64XX_UINTM)); else disable_irq_nosync(ourport->tx_irq); @@ -157,7 +192,7 @@ static void s3c24xx_serial_start_tx(struct uart_port *port) s3c24xx_serial_rx_disable(port); if (s3c24xx_serial_has_interrupt_mask(port)) - __clear_bit(S3C64XX_UINTM_TXD, + __hw_clear_bit(S3C64XX_UINTM_TXD, portaddrl(port, S3C64XX_UINTM)); else enable_irq(ourport->tx_irq); @@ -172,7 +207,7 @@ static void s3c24xx_serial_stop_rx(struct uart_port *port) if (rx_enabled(port)) { dbg("s3c24xx_serial_stop_rx: port=%p\n", port); if (s3c24xx_serial_has_interrupt_mask(port)) - __set_bit(S3C64XX_UINTM_RXD, + __hw_set_bit(S3C64XX_UINTM_RXD, portaddrl(port, S3C64XX_UINTM)); else disable_irq_nosync(ourport->rx_irq); @@ -249,6 +284,8 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id) ufcon |= S3C2410_UFCON_RESETRX; wr_regl(port, S3C2410_UFCON, ufcon); rx_enabled(port) = 1; + spin_unlock_irqrestore(&port->lock, + flags); goto out; } continue; @@ -297,10 +334,11 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id) ignore_char: continue; } + + spin_unlock_irqrestore(&port->lock, flags); tty_flip_buffer_push(&port->state->port); out: - spin_unlock_irqrestore(&port->lock, flags); return IRQ_HANDLED; } @@ -523,7 +561,7 @@ static int s3c64xx_serial_startup(struct uart_port *port) ourport->tx_claimed = 1; /* Enable Rx Interrupt */ - __clear_bit(S3C64XX_UINTM_RXD, portaddrl(port, S3C64XX_UINTM)); + __hw_clear_bit(S3C64XX_UINTM_RXD, portaddrl(port, S3C64XX_UINTM)); dbg("s3c64xx_serial_startup ok\n"); return ret; } diff --git a/drivers/tty/serial/samsung.h b/drivers/tty/serial/samsung.h index 00a499ecd385..059b54ea6184 100644 --- a/drivers/tty/serial/samsung.h +++ b/drivers/tty/serial/samsung.h @@ -71,10 +71,10 @@ struct s3c24xx_uart_port { #define portaddrl(port, reg) ((unsigned long *)((port)->membase + (reg))) #define rd_regb(port, reg) (__raw_readb(portaddr(port, reg))) -#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg))) +#define rd_regl(port, reg) (readl_relaxed(portaddr(port, reg))) #define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg)) -#define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg)) +#define wr_regl(port, reg, val) writel_relaxed(val, portaddr(port, reg)) #if defined(CONFIG_SERIAL_SAMSUNG_DEBUG) && \ defined(CONFIG_DEBUG_LL) && \ diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 379037f51a2f..42acff61cab0 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -52,6 +52,25 @@ struct s5p_ehci_hcd { #define to_s5p_ehci(hcd) (struct s5p_ehci_hcd *)(hcd_to_ehci(hcd)->priv) +static void s5p_setup_hub_gpio(struct platform_device *pdev, const char *propname, int level) +{ + int err; + int gpio; + + if (!pdev->dev.of_node) + return; + + gpio = of_get_named_gpio(pdev->dev.of_node, propname, 0); + if (!gpio_is_valid(gpio)) + return; + err = gpio_request_one(gpio, level, "ehci_vbus_gpio"); + + if (err) + dev_err(&pdev->dev, "can't request ehci hub-reset gpio %d", gpio); + else + gpio_free(gpio); +} + static void s5p_setup_vbus_gpio(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -65,10 +84,15 @@ static void s5p_setup_vbus_gpio(struct platform_device *pdev) if (!gpio_is_valid(gpio)) return; - err = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_HIGH, + err = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW, "ehci_vbus_gpio"); - if (err) + if (err) { dev_err(dev, "can't request ehci vbus gpio %d", gpio); + return; + } + + mdelay(1); + __gpio_set_value(gpio, 1); } static int s5p_ehci_probe(struct platform_device *pdev) @@ -92,8 +116,6 @@ static int s5p_ehci_probe(struct platform_device *pdev) if (!pdev->dev.coherent_dma_mask) pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - s5p_setup_vbus_gpio(pdev); - hcd = usb_create_hcd(&s5p_ehci_hc_driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { @@ -116,6 +138,8 @@ static int s5p_ehci_probe(struct platform_device *pdev) s5p_ehci->otg = phy->otg; } + s5p_setup_vbus_gpio(pdev); + s5p_ehci->clk = devm_clk_get(&pdev->dev, "usbhost"); if (IS_ERR(s5p_ehci->clk)) { @@ -154,11 +178,18 @@ static int s5p_ehci_probe(struct platform_device *pdev) if (s5p_ehci->otg) s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self); + s5p_setup_hub_gpio(pdev, "hub-reset", GPIOF_OUT_INIT_LOW); + s5p_setup_hub_gpio(pdev, "hub-connect", GPIOF_OUT_INIT_LOW); + if (s5p_ehci->phy) usb_phy_init(s5p_ehci->phy); else if (s5p_ehci->pdata->phy_init) s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST); + mdelay(1); + s5p_setup_hub_gpio(pdev, "hub-reset", GPIOF_OUT_INIT_HIGH); + s5p_setup_hub_gpio(pdev, "hub-connect", GPIOF_OUT_INIT_HIGH); + ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; |