diff options
Diffstat (limited to 'drivers')
24 files changed, 1177 insertions, 36 deletions
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 4e737728aee2..c81fe31b3da6 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 AHCI_IMX tristate "Freescale i.MX AHCI SATA support" depends on SATA_AHCI_PLATFORM && MFD_SYSCON diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 46518c622460..a4e5cf56d475 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 obj-$(CONFIG_AHCI_IMX) += ahci_imx.o # SFF w/ custom DMA diff --git a/drivers/ata/sata_exynos.c b/drivers/ata/sata_exynos.c new file mode 100644 index 000000000000..1dcfb2c8d0de --- /dev/null +++ b/drivers/ata/sata_exynos.c @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS - SATA controller platform driver wrapper + * + * 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 <linux/of_irq.h> +#include <linux/of_address.h> + +#include "ahci.h" +#include "sata_phy.h" + +#define MHZ (1000 * 1000) +#define DEFERED 1 +#define NO_PORT 0 + +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; + int irq; + unsigned int freq; + struct sata_phy *phy[]; +}; + +static int exynos_sata_parse_dt(struct device_node *np, + struct exynos_sata *sata) +{ + if (!np) + return -EINVAL; + + return of_property_read_u32(np, "samsung,sata-freq", + &sata->freq); +} + +static int 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 device_node *of_node_phy = NULL; + static int flag = 0, port_init = NO_PORT; + int n_ports, i, ret; + + sata = devm_kzalloc(dev, sizeof(*sata), GFP_KERNEL); + if (!sata) { + dev_err(dev, "can't alloc sata\n"); + return -ENOMEM; + } + + hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); + if (!hpriv) { + dev_err(dev, "can't alloc ahci_host_priv\n"); + return -ENOMEM; + } + + hpriv->flags |= (unsigned long)pi.private_data; + + sata->irq = irq_of_parse_and_map(dev->of_node, 0); + if (sata->irq <= 0) { + dev_err(dev, "irq not specified\n"); + return -EINVAL; + } + + hpriv->mmio = of_iomap(dev->of_node, 0); + if (!hpriv->mmio) { + dev_err(dev, "failed to map IO\n"); + return -ENOMEM; + } + + ret = exynos_sata_parse_dt(dev->of_node, sata); + if (ret < 0) { + dev_err(dev, "failed to get frequency for sata ctrl\n"); + goto err_iomap; + } + + 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 err_iomap; + } + + ret = clk_prepare_enable(sata->sclk); + if (ret < 0) { + dev_err(dev, "failed to enable source clk\n"); + goto err_iomap; + } + + ret = clk_set_rate(sata->sclk, sata->freq * MHZ); + if (ret < 0) { + dev_err(dev, "failed to set clk frequency\n"); + goto err_clkstrt; + } + + 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 err_clkstrt; + } + + ret = clk_prepare_enable(sata->clk); + if (ret < 0) { + dev_err(dev, "failed to enable source clk\n"); + goto err_clkstrt; + } + + 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 err_clken; + } + + 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]; + of_node_phy = of_parse_phandle(dev->of_node, + "samsung,exynos-sata-phy", i); + if (!of_node_phy) { + dev_err(dev, + "phandle of phy not found for port %d\n", i); + break; + } + + sata->phy[i] = sata_get_phy(of_node_phy); + if (IS_ERR(sata->phy[i])) { + if (PTR_ERR(sata->phy[i]) == -EBUSY) + continue; + dev_err(dev, + "failed to get sata phy for port %d\n", i); + if (flag != DEFERED) { + flag = DEFERED ; + return -EPROBE_DEFER; + } else + continue; + + } + /* Initialize the PHY */ + ret = sata_init_phy(sata->phy[i]); + if (ret < 0) { + if (ret == -EPROBE_DEFER) { + if (flag != DEFERED) { + flag = DEFERED ; + sata_put_phy(sata->phy[i]); + return -EPROBE_DEFER; + } else { + continue; + } + } else { + dev_err(dev, + "failed to initialize sata phy for port %d\n", + i); + sata_put_phy(sata->phy[i]); + } + + } + + /* 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; + + port_init++; + } + + if (port_init == NO_PORT) + goto err_initphy; + + ret = ahci_reset_controller(host); + if (ret) + goto err_rst; + + 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 err_rst; + + platform_set_drvdata(pdev, sata); + + return 0; + + err_rst: + for (i = 0; i < host->n_ports; i++) + sata_shutdown_phy(sata->phy[i]); + + err_initphy: + for (i = 0; i < host->n_ports; i++) + sata_put_phy(sata->phy[i]); + + err_clken: + clk_disable_unprepare(sata->clk); + + err_clkstrt: + clk_disable_unprepare(sata->sclk); + + err_iomap: + iounmap(hpriv->mmio); + + return ret; +} + +static int exynos_sata_remove(struct platform_device *pdev) +{ + unsigned int i; + struct device *dev = &pdev->dev; + struct ata_host *host = dev_get_drvdata(dev); + struct exynos_sata *sata = platform_get_drvdata(pdev); + struct ahci_host_priv *hpriv = + (struct ahci_host_priv *)host->private_data; + + ata_host_detach(host); + + for (i = 0; i < host->n_ports; i++) { + sata_shutdown_phy(sata->phy[i]); + sata_put_phy(sata->phy[i]); + } + iounmap(hpriv->mmio); + + 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 = "exynos-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..e9417097ffb2 --- /dev/null +++ b/drivers/ata/sata_exynos_phy.c @@ -0,0 +1,296 @@ +/* + * 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 <linux/of_address.h> + +#include <plat/cpu.h> +#include <plat/irqs.h> + +#include <mach/map.h> +#include <mach/regs-pmu.h> + +#include "sata_phy.h" +#include "sata_exynos_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; + void __iomem *pmureg; + struct clk *clk; + struct device *dev; + struct sata_phy phy; +}; + +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) +{ + unsigned long timeout = jiffies + usecs_to_jiffies(1000); + + while (time_before(jiffies, timeout)) { + if (sata_is_reg(base, reg, checkbit, status)) + return true; + } + + return false; +} + +static int exynos_sataphy_parse_dt(struct device *dev, + struct exynos_sata_phy *phy) +{ + struct device_node *sataphy_pmu; + + sataphy_pmu = of_get_child_by_name(dev->of_node, "sataphy-pmu"); + if (!sataphy_pmu) { + dev_err(dev, + "PMU control register for sata-phy not specified\n"); + return -ENODEV; + } + + phy->pmureg = of_iomap(sataphy_pmu, 0); + + of_node_put(sataphy_pmu); + + if (!phy->pmureg) { + dev_err(dev, "Can't get sata-phy pmu control register\n"); + return -EADDRNOTAVAIL; + } + + return 0; +} + +static int exynos_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 = container_of(phy, struct exynos_sata_phy, phy); + + ret = clk_prepare_enable(sata_phy->clk); + if (ret < 0) { + dev_err(phy->dev, "failed to enable source clk\n"); + return ret; + } + + if (sata_is_reg(sata_phy->mmio , EXYNOS5_SATA_CTRL0, + CTRL0_P0_PHY_CALIBRATED, CTRL0_P0_PHY_CALIBRATED)) + return 0; + + writel(SATA_PHY_PMU_EN, sata_phy->pmureg); + + 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(0x2, sata_phy->mmio + EXYNOS5_SATA_MODE0); + + ret = i2c_master_send(i2c_client, buf, sizeof(buf)); + if (ret < 0) + return -ENXIO; + + /* 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 exynos_sataphy_shutdown(struct sata_phy *phy) +{ + + struct exynos_sata_phy *sata_phy; + + sata_phy = container_of(phy, struct exynos_sata_phy, phy); + clk_disable_unprepare(sata_phy->clk); + + return 0; +} + +static int exynos_sata_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *i2c_id) +{ + i2c_client = client; + return 0; +} + +static int exynos_sata_phy_probe(struct platform_device *pdev) +{ + struct exynos_sata_phy *sataphy; + struct device *dev = &pdev->dev; + int ret = 0; + sataphy = devm_kzalloc(dev, sizeof(struct exynos_sata_phy), GFP_KERNEL); + if (!sataphy) { + dev_err(dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + sataphy->mmio = of_iomap(dev->of_node, 0); + if (!sataphy->mmio) { + dev_err(dev, "failed to remap IO\n"); + return -EADDRNOTAVAIL; + } + + ret = exynos_sataphy_parse_dt(dev, sataphy); + if (ret != 0) + goto err_iomap; + + sataphy->clk = devm_clk_get(dev, "sata_phyctrl"); + if (IS_ERR(sataphy->clk)) { + dev_err(dev, "failed to get clk for PHY\n"); + ret = PTR_ERR(sataphy->clk); + goto err_pmu; + } + + sataphy->phy.init = exynos_sataphy_init; + sataphy->phy.shutdown = exynos_sataphy_shutdown; + sataphy->phy.dev = dev; + + ret = sata_add_phy(&sataphy->phy); + if (ret < 0) { + dev_err(dev, "PHY not registered with framework\n"); + goto err_iomap; + } + + ret = i2c_add_driver(&sataphy_i2c_driver); + if (ret < 0) + goto err_phy; + + platform_set_drvdata(pdev, sataphy); + + return ret; + + err_phy: + sata_remove_phy(&sataphy->phy); + + err_pmu: + iounmap(sataphy->pmureg); + + err_iomap: + iounmap(sataphy->mmio); + + return ret; +} + +static int exynos_sata_phy_remove(struct platform_device *pdev) +{ + struct exynos_sata_phy *sataphy; + + sataphy = platform_get_drvdata(pdev); + iounmap(sataphy->mmio); + i2c_del_driver(&sataphy_i2c_driver); + sata_remove_phy(&sataphy->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 struct platform_driver sata_phy_driver = { + .probe = exynos_sata_phy_probe, + .remove = exynos_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 = (void *)phy_i2c_device_match, + }, + .probe = exynos_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_exynos_phy.h b/drivers/ata/sata_exynos_phy.h new file mode 100644 index 000000000000..f9889ccd9abc --- /dev/null +++ b/drivers/ata/sata_exynos_phy.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS - SATA PHY controller definition + * + * 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. +*/ + +#define EXYNOS5_SATA_RESET 0x4 +#define RESET_CMN_RST_N (1 << 1) +#define LINK_RESET 0xF0000 + +#define EXYNOS5_SATA_MODE0 0x10 + +#define EXYNOS5_SATA_CTRL0 0x14 +#define CTRL0_P0_PHY_CALIBRATED_SEL (1 << 9) +#define CTRL0_P0_PHY_CALIBRATED (1 << 8) + +#define EXYNOS5_SATA_PHSATA_CTRLM 0xE0 +#define PHCTRLM_REF_RATE (1 << 1) +#define PHCTRLM_HIGH_SPEED (1 << 0) + +#define EXYNOS5_SATA_PHSATA_STATM 0xF0 +#define PHSTATM_PLL_LOCKED (1 << 0) + +#define SATA_PHY_CON_RESET 0xF003F + +#define SATA_PHY_PMU_EN 0x1 + diff --git a/drivers/ata/sata_phy.c b/drivers/ata/sata_phy.c new file mode 100644 index 000000000000..53d441775d74 --- /dev/null +++ b/drivers/ata/sata_phy.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * SATA PHY framework. + * + * This file provides a set of functions/interfaces for establishing + * communication between SATA controller and the PHY controller. A + * PHY controller driver registers call backs for its initialization and + * shutdown. The SATA controller driver finds the appropriate PHYs for + * its implemented ports and initialize/shutdown PHYs through the + * call backs provided. + * + * 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(struct device_node *phy_np) +{ + struct sata_phy *phy; + unsigned long flag; + + spin_lock_irqsave(&phy_lock, flag); + + if (list_empty(&phy_list)) { + spin_unlock_irqrestore(&phy_lock, flag); + return ERR_PTR(-ENODEV); + } + + list_for_each_entry(phy, &phy_list, head) { + if (phy->dev->of_node == phy_np) { + if (phy->status == IN_USE) { + pr_info(KERN_INFO + "PHY already in use\n"); + spin_unlock_irqrestore(&phy_lock, flag); + return ERR_PTR(-EBUSY); + } + + get_device(phy->dev); + phy->status = IN_USE; + spin_unlock_irqrestore(&phy_lock, flag); + return phy; + } + } + + spin_unlock_irqrestore(&phy_lock, flag); + return ERR_PTR(-ENODEV); +} +EXPORT_SYMBOL(sata_get_phy); + +int sata_add_phy(struct sata_phy *sataphy) +{ + unsigned long flag; + unsigned int ret = -EINVAL; + struct sata_phy *phy; + + if (!sataphy) + return ret; + + spin_lock_irqsave(&phy_lock, flag); + + list_for_each_entry(phy, &phy_list, head) { + if (phy->dev->of_node == sataphy->dev->of_node) { + dev_err(sataphy->dev, "PHY already exists in the list\n"); + goto out; + } + } + + sataphy->status = NOT_IN_USE; + list_add_tail(&sataphy->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 *sataphy) +{ + unsigned long flag; + struct sata_phy *phy; + + if (!sataphy) + return; + + if (sataphy->status == IN_USE) { + pr_info(KERN_INFO + "PHY in use, cannot be removed\n"); + return; + } + + spin_lock_irqsave(&phy_lock, flag); + + list_for_each_entry(phy, &phy_list, head) { + if (phy->dev->of_node == sataphy->dev->of_node) + list_del(&phy->head); + } + + spin_unlock_irqrestore(&phy_lock, flag); +} +EXPORT_SYMBOL(sata_remove_phy); + +void sata_put_phy(struct sata_phy *sataphy) +{ + unsigned long flag; + + if (!sataphy) + return; + + spin_lock_irqsave(&phy_lock, flag); + + put_device(sataphy->dev); + sataphy->status = NOT_IN_USE; + + spin_unlock_irqrestore(&phy_lock, flag); +} +EXPORT_SYMBOL(sata_put_phy); + +int sata_init_phy(struct sata_phy *sataphy) +{ + if (sataphy && sataphy->init) + return sataphy->init(sataphy); + + return -EINVAL; +} +EXPORT_SYMBOL(sata_init_phy); + +void sata_shutdown_phy(struct sata_phy *sataphy) +{ + if (sataphy && sataphy->shutdown) + sataphy->shutdown(sataphy); +} +EXPORT_SYMBOL(sata_shutdown_phy); + diff --git a/drivers/ata/sata_phy.h b/drivers/ata/sata_phy.h new file mode 100644 index 000000000000..9ed1dbed5a11 --- /dev/null +++ b/drivers/ata/sata_phy.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * 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. +*/ + +#define IN_USE 1 +#define NOT_IN_USE 0 + +struct sata_phy { + int (*init) (struct sata_phy *); + int (*shutdown) (struct sata_phy *); + struct device *dev; + void *priv_data; + struct list_head head; + unsigned char status; +}; + +struct sata_phy *sata_get_phy(struct device_node *); +int sata_add_phy(struct sata_phy *); +void sata_remove_phy(struct sata_phy *); +void sata_put_phy(struct sata_phy *); +int sata_init_phy(struct sata_phy *); +void sata_shutdown_phy(struct sata_phy *); + diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index adf32343c9f9..4576f1301162 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -35,6 +35,7 @@ #define GPLL_CON0 0x10150 #define SRC_TOP0 0x10210 #define SRC_TOP2 0x10218 +#define SRC_TOP3 0x1021C #define SRC_GSCL 0x10220 #define SRC_DISP1_0 0x1022c #define SRC_MAU 0x10240 @@ -120,7 +121,7 @@ enum exynos5250_clks { spi2, i2s1, i2s2, pcm1, pcm2, pwm, spdif, ac97, hsi2c0, hsi2c1, hsi2c2, hsi2c3, chipid, sysreg, pmu, cmu_top, cmu_core, cmu_mem, tzpc0, tzpc1, tzpc2, tzpc3, tzpc4, tzpc5, tzpc6, tzpc7, tzpc8, tzpc9, hdmi_cec, mct, - wdt, rtc, tmu, fimd1, mie1, dsim0, dp, mixer, hdmi, g2d, + wdt, rtc, tmu, fimd1, mie1, dsim0, dp, mixer, hdmi, g2d, smmu_mixer, /* mux clocks */ mout_hdmi = 1024, @@ -194,6 +195,7 @@ PNAME(mout_mpll_user_p) = { "fin_pll", "sclk_mpll" }; PNAME(mout_bpll_user_p) = { "fin_pll", "sclk_bpll" }; PNAME(mout_aclk166_p) = { "sclk_cpll", "sclk_mpll_user" }; PNAME(mout_aclk200_p) = { "sclk_mpll_user", "sclk_bpll_user" }; +PNAME(mout_aclk200_disp1_sub_p) = { "fin_pll", "aclk200" }; PNAME(mout_hdmi_p) = { "div_hdmi_pixel", "sclk_hdmiphy" }; PNAME(mout_usb3_p) = { "sclk_mpll_user", "sclk_cpll" }; PNAME(mout_group1_p) = { "fin_pll", "fin_pll", "sclk_hdmi27m", @@ -252,6 +254,8 @@ static struct samsung_mux_clock exynos5250_mux_clks[] __initdata = { MUX(none, "mout_aclk166", mout_aclk166_p, SRC_TOP0, 8, 1), MUX(none, "mout_aclk333", mout_aclk166_p, SRC_TOP0, 16, 1), MUX(none, "mout_aclk200", mout_aclk200_p, SRC_TOP0, 12, 1), + MUX(none, "mout_aclk200_disp1", mout_aclk200_disp1_sub_p, + SRC_TOP3, 4, 1), MUX(none, "mout_cam_bayer", mout_group1_p, SRC_GSCL, 12, 4), MUX(none, "mout_cam0", mout_group1_p, SRC_GSCL, 16, 4), MUX(none, "mout_cam1", mout_group1_p, SRC_GSCL, 20, 4), @@ -353,6 +357,7 @@ static struct samsung_gate_clock exynos5250_gate_clks[] __initdata = { GATE(smmu_gscl1, "smmu_gscl1", "aclk266", GATE_IP_GSCL, 8, 0, 0), GATE(smmu_gscl2, "smmu_gscl2", "aclk266", GATE_IP_GSCL, 9, 0, 0), GATE(smmu_gscl3, "smmu_gscl3", "aclk266", GATE_IP_GSCL, 10, 0, 0), + GATE(smmu_mixer, "smmu_mixer", "mout_aclk200_disp1", GATE_IP_DISP1, 9, 0, 0), GATE(mfc, "mfc", "aclk333", GATE_IP_MFC, 0, 0, 0), GATE(smmu_mfcl, "smmu_mfcl", "aclk333", GATE_IP_MFC, 1, 0, 0), GATE(smmu_mfcr, "smmu_mfcr", "aclk333", GATE_IP_MFC, 2, 0, 0), diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index 5b34768f4d7c..36c32254dcf9 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -27,6 +27,8 @@ #include <asm/mach/time.h> +#include <plat/cpu.h> + #define EXYNOS4_MCTREG(x) (x) #define EXYNOS4_MCT_G_CNT_L EXYNOS4_MCTREG(0x100) #define EXYNOS4_MCT_G_CNT_U EXYNOS4_MCTREG(0x104) @@ -194,6 +196,10 @@ static void __init exynos4_clocksource_init(void) { exynos4_mct_frc_start(0, 0); + if (soc_is_exynos5250()) { + mct_frc.rating = 399; + } + if (clocksource_register_hz(&mct_frc, clk_rate)) panic("%s: can't register clocksource\n", mct_frc.name); } diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index 0fac34439e31..4a3462851299 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -17,6 +17,8 @@ #include <linux/regulator/consumer.h> #include <linux/cpufreq.h> #include <linux/suspend.h> +#include <linux/notifier.h> +#include <linux/reboot.h> #include <plat/cpu.h> @@ -245,8 +247,35 @@ static struct notifier_block exynos_cpufreq_nb = { .notifier_call = exynos_cpufreq_pm_notifier, }; +static int exynos_cpufreq_reboot_notifier(struct notifier_block *this, + unsigned long code, void *_cmd) +{ + struct cpufreq_policy *policy = cpufreq_cpu_get(0); /* boot CPU */ + mutex_lock(&cpufreq_lock); + + if (frequency_locked) + goto out; + frequency_locked = true; + + if (locking_frequency) { + mutex_unlock(&cpufreq_lock); + exynos_target(policy, locking_frequency, CPUFREQ_RELATION_H); + mutex_lock(&cpufreq_lock); + } + +out: + mutex_unlock(&cpufreq_lock); + return NOTIFY_DONE; +} + +static struct notifier_block exynos_cpufreq_reboot_nb = { + .notifier_call = exynos_cpufreq_reboot_notifier, +}; + static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy) { + int ret; + policy->cur = policy->min = policy->max = exynos_getspeed(policy->cpu); cpufreq_frequency_table_get_attr(exynos_info->freq_table, policy->cpu); @@ -256,7 +285,13 @@ static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy) cpumask_setall(policy->cpus); - return cpufreq_frequency_table_cpuinfo(policy, exynos_info->freq_table); + ret = cpufreq_frequency_table_cpuinfo(policy, exynos_info->freq_table); + if (ret) + return ret; + + cpufreq_frequency_table_get_attr(exynos_info->freq_table, policy->cpu); + return 0; + } static int exynos_cpufreq_cpu_exit(struct cpufreq_policy *policy) @@ -319,6 +354,7 @@ static int __init exynos_cpufreq_init(void) locking_frequency = exynos_getspeed(0); register_pm_notifier(&exynos_cpufreq_nb); + register_reboot_notifier(&exynos_cpufreq_reboot_nb); if (cpufreq_register_driver(&exynos_driver)) { pr_err("%s: failed to register cpufreq driver\n", __func__); diff --git a/drivers/gpu/drm/exynos/exynos_ddc.c b/drivers/gpu/drm/exynos/exynos_ddc.c index 6a8c84e7c839..719610aae834 100644 --- a/drivers/gpu/drm/exynos/exynos_ddc.c +++ b/drivers/gpu/drm/exynos/exynos_ddc.c @@ -51,12 +51,18 @@ static struct of_device_id hdmiddc_match_types[] = { } }; +static const struct i2c_device_id hdmiddc_id[] = { + { "exynos5-hdmiddc", 0 }, + { "exynos4210-hdmiddc", 0 }, +}; + struct i2c_driver ddc_driver = { .driver = { .name = "exynos-hdmiddc", .owner = THIS_MODULE, .of_match_table = hdmiddc_match_types, }, + .id_table = hdmiddc_id, .probe = s5p_ddc_probe, .remove = s5p_ddc_remove, .command = NULL, diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index bb82ef78ca85..a645a299d631 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -286,7 +286,8 @@ static struct drm_driver exynos_drm_driver = { static int exynos_drm_platform_probe(struct platform_device *pdev) { - pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + if (!pdev->dev.coherent_dma_mask) + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); return drm_platform_init(&exynos_drm_driver, pdev); } diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index a0e10aeb0e67..64560d38abd2 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -34,6 +34,7 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/of_gpio.h> +#include <linux/of_address.h> #include <drm/exynos_drm.h> @@ -82,7 +83,6 @@ struct hdmi_resources { struct clk *sclk_hdmi; struct clk *sclk_pixel; struct clk *sclk_hdmiphy; - struct clk *hdmiphy; struct clk *mout_hdmi; struct regulator_bulk_data *regul_bulk; int regul_count; @@ -189,6 +189,7 @@ struct hdmi_context { struct mutex hdmi_mutex; void __iomem *regs; + void __iomem *phy_pow_ctrl_reg; void *parent_ctx; int irq; @@ -404,6 +405,14 @@ static inline void hdmi_reg_writemask(struct hdmi_context *hdata, writel(value, hdata->regs + reg_id); } +static inline void hdmi_phy_pow_ctrl_reg_writemask(struct hdmi_context *hdata, + u32 value, u32 mask) +{ + u32 old = readl(hdata->phy_pow_ctrl_reg); + value = (value & mask) | (old & ~mask); + writel(value, hdata->phy_pow_ctrl_reg); +} + static void hdmi_v13_regs_dump(struct hdmi_context *hdata, char *prefix) { #define DUMPREG(reg_id) \ @@ -1686,7 +1695,8 @@ static void hdmi_poweron(struct hdmi_context *hdata) if (regulator_bulk_enable(res->regul_count, res->regul_bulk)) DRM_DEBUG_KMS("failed to enable regulator bulk\n"); - clk_prepare_enable(res->hdmiphy); + hdmi_phy_pow_ctrl_reg_writemask(hdata, PMU_HDMI_PHY_ENABLE, + PMU_HDMI_PHY_CONTROL_MASK); clk_prepare_enable(res->hdmi); clk_prepare_enable(res->sclk_hdmi); @@ -1711,7 +1721,8 @@ static void hdmi_poweroff(struct hdmi_context *hdata) clk_disable_unprepare(res->sclk_hdmi); clk_disable_unprepare(res->hdmi); - clk_disable_unprepare(res->hdmiphy); + hdmi_phy_pow_ctrl_reg_writemask(hdata, PMU_HDMI_PHY_DISABLE, + PMU_HDMI_PHY_CONTROL_MASK); regulator_bulk_disable(res->regul_count, res->regul_bulk); mutex_lock(&hdata->hdmi_mutex); @@ -1764,7 +1775,7 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg) struct hdmi_context *hdata = ctx->ctx; mutex_lock(&hdata->hdmi_mutex); - hdata->hpd = gpio_get_value(hdata->hpd_gpio); + hdata->hpd = true; /* gpio_get_value(hdata->hpd_gpio); */ mutex_unlock(&hdata->hdmi_mutex); if (ctx->drm_dev) @@ -1810,11 +1821,6 @@ static int hdmi_resources_init(struct hdmi_context *hdata) DRM_ERROR("failed to get clock 'sclk_hdmiphy'\n"); goto fail; } - res->hdmiphy = devm_clk_get(dev, "hdmiphy"); - if (IS_ERR(res->hdmiphy)) { - DRM_ERROR("failed to get clock 'hdmiphy'\n"); - goto fail; - } res->mout_hdmi = devm_clk_get(dev, "mout_hdmi"); if (IS_ERR(res->mout_hdmi)) { DRM_ERROR("failed to get clock 'mout_hdmi'\n"); @@ -1838,6 +1844,13 @@ static int hdmi_resources_init(struct hdmi_context *hdata) } res->regul_count = ARRAY_SIZE(supply); + clk_prepare(res->hdmi); + clk_prepare(res->sclk_hdmi); + clk_prepare(res->sclk_pixel); + clk_prepare(res->sclk_hdmiphy); + + clk_set_parent(res->sclk_hdmi, res->sclk_pixel); + return 0; fail: DRM_ERROR("HDMI resource init - failed\n"); @@ -1882,6 +1895,39 @@ err_data: return NULL; } +static int drm_hdmi_dt_parse_phy_pow_control(struct hdmi_context *hdata) +{ + struct device_node *phy_pow_ctrl_node; + u32 buf[2]; + int ret = 0; + + phy_pow_ctrl_node = of_find_node_by_name(NULL, "phy-power-control"); + if (!phy_pow_ctrl_node) { + DRM_ERROR("Failed to find phy power control node\n"); + ret = -ENODEV; + goto fail; + } + + /* reg property holds two informations: addr of pmu register, size */ + if (of_property_read_u32_array(phy_pow_ctrl_node, "reg", + (u32 *)&buf, 2)) { + DRM_ERROR("faild to get phy power control reg\n"); + ret = -EINVAL; + goto fail; + } + + hdata->phy_pow_ctrl_reg = devm_ioremap(hdata->dev, buf[0], buf[1]); + if (!hdata->phy_pow_ctrl_reg) { + DRM_ERROR("failed to ioremap phy pmu reg\n"); + ret = -ENOMEM; + goto fail; + } + +fail: + of_node_put(phy_pow_ctrl_node); + return ret; +} + static struct of_device_id hdmi_match_types[] = { { .compatible = "samsung,exynos5-hdmi", @@ -1941,20 +1987,30 @@ static int hdmi_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hdata->regs = devm_ioremap_resource(dev, res); - if (IS_ERR(hdata->regs)) - return PTR_ERR(hdata->regs); + hdata->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hdata->regs)) { + ret = PTR_ERR(hdata->regs); + goto err_clk_res; + } ret = devm_gpio_request(dev, hdata->hpd_gpio, "HPD"); if (ret) { DRM_ERROR("failed to request HPD gpio\n"); + goto err_clk_res; + } + + /* map hdmiphy power control reg */ + ret = drm_hdmi_dt_parse_phy_pow_control(hdata); + if (ret) { + DRM_ERROR("failed to map phy power control registers\n"); return ret; } /* DDC i2c driver */ if (i2c_add_driver(&ddc_driver)) { DRM_ERROR("failed to register ddc i2c driver\n"); - return -ENOENT; + ret = -ENOENT; + goto err_clk_res; } hdata->ddc_port = hdmi_ddc; @@ -1975,7 +2031,7 @@ static int hdmi_probe(struct platform_device *pdev) goto err_hdmiphy; } - hdata->hpd = gpio_get_value(hdata->hpd_gpio); + hdata->hpd = true; /* gpio_get_value(hdata->hpd_gpio); */ ret = devm_request_threaded_irq(dev, hdata->irq, NULL, hdmi_irq_thread, IRQF_TRIGGER_RISING | @@ -2000,12 +2056,19 @@ err_hdmiphy: i2c_del_driver(&hdmiphy_driver); err_ddc: i2c_del_driver(&ddc_driver); +err_clk_res: + clk_unprepare(hdata->res.hdmi); + clk_unprepare(hdata->res.sclk_hdmi); + clk_unprepare(hdata->res.sclk_pixel); + clk_unprepare(hdata->res.sclk_hdmiphy); return ret; } static int hdmi_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); + struct hdmi_context *hdata = ctx->ctx; pm_runtime_disable(dev); @@ -2014,9 +2077,22 @@ static int hdmi_remove(struct platform_device *pdev) /* DDC i2c driver */ i2c_del_driver(&ddc_driver); + clk_unprepare(hdata->res.hdmi); + clk_unprepare(hdata->res.sclk_hdmi); + clk_unprepare(hdata->res.sclk_pixel); + clk_unprepare(hdata->res.sclk_hdmiphy); return 0; } +static void hdmi_shutdown(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); + struct hdmi_context *hdata = ctx->ctx; + + hdmi_poweroff(hdata); +} + #ifdef CONFIG_PM_SLEEP static int hdmi_suspend(struct device *dev) { @@ -2044,7 +2120,7 @@ static int hdmi_resume(struct device *dev) struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); struct hdmi_context *hdata = ctx->ctx; - hdata->hpd = gpio_get_value(hdata->hpd_gpio); + hdata->hpd = true; /* gpio_get_value(hdata->hpd_gpio); */ enable_irq(hdata->irq); @@ -2089,6 +2165,7 @@ static const struct dev_pm_ops hdmi_pm_ops = { struct platform_driver hdmi_driver = { .probe = hdmi_probe, .remove = hdmi_remove, + .shutdown = hdmi_shutdown, .driver = { .name = "exynos-hdmi", .owner = THIS_MODULE, diff --git a/drivers/gpu/drm/exynos/exynos_hdmiphy.c b/drivers/gpu/drm/exynos/exynos_hdmiphy.c index 59abb1494ceb..38a6496ed695 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmiphy.c +++ b/drivers/gpu/drm/exynos/exynos_hdmiphy.c @@ -24,6 +24,8 @@ static int hdmiphy_probe(struct i2c_client *client, const struct i2c_device_id *id) { + printk("trb: %s() called\n", __func__); + hdmi_attach_hdmiphy_client(client); dev_info(&client->adapter->dev, "attached s5p_hdmiphy " @@ -52,14 +54,22 @@ static struct of_device_id hdmiphy_match_types[] = { } }; +static const struct i2c_device_id hdmiphy_id[] = { + { "exynos5-hdmiphy", 0 }, + { "exynos4210-hdmiphy", 0 }, + { "exynos4212-hdmiphy", 0 }, +}; + struct i2c_driver hdmiphy_driver = { .driver = { .name = "exynos-hdmiphy", .owner = THIS_MODULE, .of_match_table = hdmiphy_match_types, }, + .id_table = hdmiphy_id, .probe = hdmiphy_probe, .remove = hdmiphy_remove, .command = NULL, }; + EXPORT_SYMBOL(hdmiphy_driver); diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 63bc5f92fbb3..e80fb0dedf50 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -1086,6 +1086,9 @@ static int mixer_resources_init(struct exynos_drm_hdmi_context *ctx, } mixer_res->irq = res->start; + clk_prepare(mixer_res->mixer); + clk_prepare(mixer_res->sclk_hdmi); + return 0; } @@ -1113,9 +1116,6 @@ static int vp_resources_init(struct exynos_drm_hdmi_context *ctx, return -ENODEV; } - if (mixer_res->sclk_hdmi) - clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi); - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (res == NULL) { dev_err(dev, "get memory resource failed.\n"); @@ -1129,6 +1129,13 @@ static int vp_resources_init(struct exynos_drm_hdmi_context *ctx, return -ENXIO; } + clk_prepare(mixer_res->vp); + clk_prepare(mixer_res->sclk_mixer); + clk_prepare(mixer_res->sclk_dac); + + if (mixer_res->sclk_hdmi) + clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi); + return 0; } @@ -1226,7 +1233,7 @@ static int mixer_probe(struct platform_device *pdev) ret = vp_resources_init(drm_hdmi_ctx, pdev); if (ret) { DRM_ERROR("vp_resources_init failed\n"); - goto fail; + goto out_vp; } } @@ -1240,7 +1247,9 @@ static int mixer_probe(struct platform_device *pdev) return 0; - +out_vp: + clk_unprepare(ctx->mixer_res.sclk_hdmi); + clk_unprepare(ctx->mixer_res.mixer); fail: dev_info(dev, "probe failed\n"); return ret; @@ -1248,10 +1257,23 @@ fail: static int mixer_remove(struct platform_device *pdev) { + struct exynos_drm_hdmi_context *drm_hdmi_ctx = + get_mixer_context(&pdev->dev); + struct mixer_context *ctx = drm_hdmi_ctx->ctx; + dev_info(&pdev->dev, "remove successful\n"); pm_runtime_disable(&pdev->dev); + clk_unprepare(ctx->mixer_res.mixer); + clk_unprepare(ctx->mixer_res.sclk_hdmi); + + if (ctx->vp_enabled) { + clk_unprepare(ctx->mixer_res.vp); + clk_unprepare(ctx->mixer_res.sclk_mixer); + clk_unprepare(ctx->mixer_res.sclk_dac); + } + return 0; } diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h b/drivers/gpu/drm/exynos/regs-hdmi.h index ef1b3eb3ba6e..8d9ca2543ed8 100644 --- a/drivers/gpu/drm/exynos/regs-hdmi.h +++ b/drivers/gpu/drm/exynos/regs-hdmi.h @@ -578,4 +578,8 @@ #define HDMI_TG_VACT_ST4_H HDMI_TG_BASE(0x0074) #define HDMI_TG_3D HDMI_TG_BASE(0x00F0) +#define PMU_HDMI_PHY_CONTROL_MASK (1 << 0) +#define PMU_HDMI_PHY_ENABLE (1) +#define PMU_HDMI_PHY_DISABLE (0) + #endif /* SAMSUNG_REGS_HDMI_H */ diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 3535f3c0f7b4..2b279d070e2a 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -85,6 +85,7 @@ #define QUIRK_S3C2440 (1 << 0) #define QUIRK_HDMIPHY (1 << 1) #define QUIRK_NO_GPIO (1 << 2) +#define QUIRK_POLL (1 << 3) /* Max time to wait for bus to become idle after a xfer (in us) */ #define S3C2410_IDLE_TIMEOUT 5000 @@ -141,6 +142,8 @@ 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 }, @@ -149,6 +152,8 @@ static const struct of_device_id s3c24xx_i2c_match[] = { .data = (void *)(QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO) }, { .compatible = "samsung,exynos5440-i2c", .data = (void *)(QUIRK_S3C2440 | QUIRK_NO_GPIO) }, + { .compatible = "samsung,exynos5-sata-phy-i2c", + .data = (void *)(QUIRK_S3C2440 | QUIRK_POLL | QUIRK_NO_GPIO) }, {}, }; MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match); @@ -187,7 +192,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_POLL)) + wake_up(&i2c->wait); } static inline void s3c24xx_i2c_disable_ack(struct s3c24xx_i2c *i2c) @@ -224,6 +230,22 @@ static inline void s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c) writel(tmp | S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON); } +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 * @@ -268,6 +290,16 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, stat |= S3C2410_IICSTAT_START; writel(stat, i2c->regs + S3C2410_IICSTAT); + + if (i2c->quirks & QUIRK_POLL) { + 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) @@ -675,6 +707,15 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, s3c24xx_i2c_enable_irq(i2c); s3c24xx_i2c_message_start(i2c, msgs); + if (i2c->quirks & QUIRK_POLL) { + ret = i2c->msg_idx; + + if (ret != num) + dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret); + + goto out; + } + timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); ret = i2c->msg_idx; @@ -820,6 +861,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_POLL) + iiccon |= S3C2410_IICCON_SCALE(2); + writel(iiccon, i2c->regs + S3C2410_IICCON); if (i2c->quirks & QUIRK_S3C2440) { @@ -1117,18 +1161,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_POLL)) { + 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/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 8faf9691712d..457d1c690789 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -197,6 +197,16 @@ static struct mfc_control controls[] = { .default_value = 1, .is_volatile = 1, }, + { + .id = V4L2_CID_CODEC_FRAME_TAG, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Frame Tag", + .minimum = 0, + .maximum = INT_MAX, + .step = 1, + .default_value = 0, + .is_volatile = 1, + }, }; #define NUM_CTRLS ARRAY_SIZE(controls) @@ -719,6 +729,9 @@ static int s5p_mfc_dec_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE: ctx->slice_interface = ctrl->val; break; + case V4L2_CID_CODEC_FRAME_TAG: + ctx->frame_tag = ctrl->val; + break; default: mfc_err("Invalid control 0x%08x\n", ctrl->id); return -EINVAL; @@ -753,6 +766,9 @@ static int s5p_mfc_dec_g_v_ctrl(struct v4l2_ctrl *ctrl) return -EINVAL; } break; + case V4L2_CID_CODEC_FRAME_TAG: + ctrl->val = s5p_mfc_hw_call(dev->mfc_ops, get_frame_tag, ctx); + break; } return 0; } diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h index 754c540e7a7e..498ff09a8158 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h @@ -77,6 +77,7 @@ struct s5p_mfc_hw_ops { unsigned int (*get_pic_type_bot)(struct s5p_mfc_ctx *ctx); unsigned int (*get_crop_info_h)(struct s5p_mfc_ctx *ctx); unsigned int (*get_crop_info_v)(struct s5p_mfc_ctx *ctx); + unsigned int (*get_frame_tag)(struct s5p_mfc_ctx *ctx); }; void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c index 368582b091bf..c7408bcffa6f 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -1075,6 +1075,9 @@ static void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush) dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) & ~(S5P_FIMV_DPB_FLUSH_MASK << S5P_FIMV_DPB_FLUSH_SHIFT); mfc_write(dev, dpb, S5P_FIMV_SI_CH0_DPB_CONF_CTRL); + + s5p_mfc_write_info_v5(ctx, ctx->frame_tag, + S5P_FIMV_SHARED_SET_FRAME_TAG); } /* Decode a single frame */ @@ -1682,6 +1685,11 @@ static unsigned int s5p_mfc_get_crop_info_v_v5(struct s5p_mfc_ctx *ctx) return s5p_mfc_read_info_v5(ctx, CROP_INFO_V); } +unsigned int s5p_mfc_get_frame_tag(struct s5p_mfc_ctx *ctx) +{ + return s5p_mfc_read_info_v5(ctx, GET_FRAME_TAG_TOP); +} + /* Initialize opr function pointers for MFC v5 */ static struct s5p_mfc_hw_ops s5p_mfc_ops_v5 = { .alloc_dec_temp_buffers = s5p_mfc_alloc_dec_temp_buffers_v5, @@ -1735,6 +1743,7 @@ static struct s5p_mfc_hw_ops s5p_mfc_ops_v5 = { .get_pic_type_bot = s5p_mfc_get_pic_type_bot_v5, .get_crop_info_h = s5p_mfc_get_crop_info_h_v5, .get_crop_info_v = s5p_mfc_get_crop_info_v_v5, + .get_frame_tag = s5p_mfc_get_frame_tag, }; struct s5p_mfc_hw_ops *s5p_mfc_init_hw_ops_v5(void) diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index c3f080388684..2f4ec8b2256f 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -736,6 +736,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_DEC_FRAME: return "Video Decoder Frame Count"; case V4L2_CID_MPEG_VIDEO_VBV_DELAY: return "Initial Delay for VBV Control"; case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER: return "Repeat Sequence Header"; + case V4L2_CID_CODEC_FRAME_TAG: return "Video Decoder Frame Tag"; /* VPX controls */ case V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS: return "VPX Number of Partitions"; @@ -1022,6 +1023,12 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *flags |= V4L2_CTRL_FLAG_READ_ONLY; *min = *max = *step = *def = 0; break; + case V4L2_CID_CODEC_FRAME_TAG: + *type = V4L2_CTRL_TYPE_INTEGER; + *step = 1; + *min = 0; + *max = INT_MAX; + break; default: *type = V4L2_CTRL_TYPE_INTEGER; break; diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index 386a3df53678..c07856729c4b 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/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index f3dfa19a1cb8..f7f53404971f 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -531,6 +531,28 @@ static int s3c64xx_serial_startup(struct uart_port *port) return ret; } +static void s3c64xx_serial_shutdown(struct uart_port *port) +{ + struct s3c24xx_uart_port *ourport = to_ourport(port); + + if (ourport->tx_claimed) { + free_irq(port->irq, ourport); + tx_enabled(port) = 0; + ourport->tx_claimed = 0; + } + + if (ourport->rx_claimed) { + ourport->rx_claimed = 0; + rx_enabled(port) = 0; + } + + /* Clear pending interrupts and mask all interrupts */ + if (s3c24xx_serial_has_interrupt_mask(port)) { + wr_regl(port, S3C64XX_UINTP, 0xf); + wr_regl(port, S3C64XX_UINTM, 0xf); + } +} + /* power power management control */ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, @@ -1016,6 +1038,9 @@ static void s3c24xx_serial_resetport(struct uart_port *port, wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); wr_regl(port, S3C2410_UFCON, cfg->ufcon); + wr_regl(port, S3C64XX_UINTM, 0xf); + wr_regl(port, S3C64XX_UINTP, 0xf); + /* some delay is required after fifo reset */ udelay(1); } @@ -1128,8 +1153,10 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, port->dev = &platdev->dev; /* Startup sequence is different for s3c64xx and higher SoC's */ - if (s3c24xx_serial_has_interrupt_mask(port)) + if (s3c24xx_serial_has_interrupt_mask(port)) { s3c24xx_serial_ops.startup = s3c64xx_serial_startup; + s3c24xx_serial_ops.shutdown = s3c64xx_serial_shutdown; + } port->uartclk = 1; diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index a31641e18d19..ad8368d96665 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -339,7 +339,7 @@ static int __init usb3503_init(void) return 0; } -module_init(usb3503_init); +late_initcall(usb3503_init); static void __exit usb3503_exit(void) { |