NVMe: Only release requested regions
[linux-2.6-block.git] / drivers / phy / phy-exynos-mipi-video.c
index 2a54caba93b4021a5b48c44100ec42e9baf45da4..cc093ebfda9435a28ec627ca1f022b877c42b342 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
  *
- * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Copyright (C) 2013,2016 Samsung Electronics Co., Ltd.
  * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
  *
  * This program is free software; you can redistribute it and/or modify
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/mfd/syscon/exynos4-pmu.h>
+#include <linux/mfd/syscon/exynos5-pmu.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_device.h>
 #include <linux/phy/phy.h>
-#include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/spinlock.h>
 #include <linux/mfd/syscon.h>
 
-/* MIPI_PHYn_CONTROL reg. offset (for base address from ioremap): n = 0..1 */
-#define EXYNOS_MIPI_PHY_CONTROL(n)     ((n) * 4)
-
 enum exynos_mipi_phy_id {
+       EXYNOS_MIPI_PHY_ID_NONE = -1,
        EXYNOS_MIPI_PHY_ID_CSIS0,
        EXYNOS_MIPI_PHY_ID_DSIM0,
        EXYNOS_MIPI_PHY_ID_CSIS1,
        EXYNOS_MIPI_PHY_ID_DSIM1,
+       EXYNOS_MIPI_PHY_ID_CSIS2,
        EXYNOS_MIPI_PHYS_NUM
 };
 
-#define is_mipi_dsim_phy_id(id) \
-       ((id) == EXYNOS_MIPI_PHY_ID_DSIM0 || (id) == EXYNOS_MIPI_PHY_ID_DSIM1)
+enum exynos_mipi_phy_regmap_id {
+       EXYNOS_MIPI_REGMAP_PMU,
+       EXYNOS_MIPI_REGMAP_DISP,
+       EXYNOS_MIPI_REGMAP_CAM0,
+       EXYNOS_MIPI_REGMAP_CAM1,
+       EXYNOS_MIPI_REGMAPS_NUM
+};
+
+struct mipi_phy_device_desc {
+       int num_phys;
+       int num_regmaps;
+       const char *regmap_names[EXYNOS_MIPI_REGMAPS_NUM];
+       struct exynos_mipi_phy_desc {
+               enum exynos_mipi_phy_id coupled_phy_id;
+               u32 enable_val;
+               unsigned int enable_reg;
+               enum exynos_mipi_phy_regmap_id enable_map;
+               u32 resetn_val;
+               unsigned int resetn_reg;
+               enum exynos_mipi_phy_regmap_id resetn_map;
+       } phys[EXYNOS_MIPI_PHYS_NUM];
+};
+
+static const struct mipi_phy_device_desc s5pv210_mipi_phy = {
+       .num_regmaps = 1,
+       .regmap_names = {"syscon"},
+       .num_phys = 4,
+       .phys = {
+               {
+                       /* EXYNOS_MIPI_PHY_ID_CSIS0 */
+                       .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0,
+                       .enable_val = EXYNOS4_MIPI_PHY_ENABLE,
+                       .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0),
+                       .enable_map = EXYNOS_MIPI_REGMAP_PMU,
+                       .resetn_val = EXYNOS4_MIPI_PHY_SRESETN,
+                       .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0),
+                       .resetn_map = EXYNOS_MIPI_REGMAP_PMU,
+               }, {
+                       /* EXYNOS_MIPI_PHY_ID_DSIM0 */
+                       .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0,
+                       .enable_val = EXYNOS4_MIPI_PHY_ENABLE,
+                       .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0),
+                       .enable_map = EXYNOS_MIPI_REGMAP_PMU,
+                       .resetn_val = EXYNOS4_MIPI_PHY_MRESETN,
+                       .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0),
+                       .resetn_map = EXYNOS_MIPI_REGMAP_PMU,
+               }, {
+                       /* EXYNOS_MIPI_PHY_ID_CSIS1 */
+                       .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1,
+                       .enable_val = EXYNOS4_MIPI_PHY_ENABLE,
+                       .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1),
+                       .enable_map = EXYNOS_MIPI_REGMAP_PMU,
+                       .resetn_val = EXYNOS4_MIPI_PHY_SRESETN,
+                       .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1),
+                       .resetn_map = EXYNOS_MIPI_REGMAP_PMU,
+               }, {
+                       /* EXYNOS_MIPI_PHY_ID_DSIM1 */
+                       .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1,
+                       .enable_val = EXYNOS4_MIPI_PHY_ENABLE,
+                       .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1),
+                       .enable_map = EXYNOS_MIPI_REGMAP_PMU,
+                       .resetn_val = EXYNOS4_MIPI_PHY_MRESETN,
+                       .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1),
+                       .resetn_map = EXYNOS_MIPI_REGMAP_PMU,
+               },
+       },
+};
+
+static const struct mipi_phy_device_desc exynos5420_mipi_phy = {
+       .num_regmaps = 1,
+       .regmap_names = {"syscon"},
+       .num_phys = 5,
+       .phys = {
+               {
+                       /* EXYNOS_MIPI_PHY_ID_CSIS0 */
+                       .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0,
+                       .enable_val = EXYNOS5_PHY_ENABLE,
+                       .enable_reg = EXYNOS5420_MIPI_PHY0_CONTROL,
+                       .enable_map = EXYNOS_MIPI_REGMAP_PMU,
+                       .resetn_val = EXYNOS5_MIPI_PHY_S_RESETN,
+                       .resetn_reg = EXYNOS5420_MIPI_PHY0_CONTROL,
+                       .resetn_map = EXYNOS_MIPI_REGMAP_PMU,
+               }, {
+                       /* EXYNOS_MIPI_PHY_ID_DSIM0 */
+                       .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0,
+                       .enable_val = EXYNOS5_PHY_ENABLE,
+                       .enable_reg = EXYNOS5420_MIPI_PHY0_CONTROL,
+                       .enable_map = EXYNOS_MIPI_REGMAP_PMU,
+                       .resetn_val = EXYNOS5_MIPI_PHY_M_RESETN,
+                       .resetn_reg = EXYNOS5420_MIPI_PHY0_CONTROL,
+                       .resetn_map = EXYNOS_MIPI_REGMAP_PMU,
+               }, {
+                       /* EXYNOS_MIPI_PHY_ID_CSIS1 */
+                       .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1,
+                       .enable_val = EXYNOS5_PHY_ENABLE,
+                       .enable_reg = EXYNOS5420_MIPI_PHY1_CONTROL,
+                       .enable_map = EXYNOS_MIPI_REGMAP_PMU,
+                       .resetn_val = EXYNOS5_MIPI_PHY_S_RESETN,
+                       .resetn_reg = EXYNOS5420_MIPI_PHY1_CONTROL,
+                       .resetn_map = EXYNOS_MIPI_REGMAP_PMU,
+               }, {
+                       /* EXYNOS_MIPI_PHY_ID_DSIM1 */
+                       .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1,
+                       .enable_val = EXYNOS5_PHY_ENABLE,
+                       .enable_reg = EXYNOS5420_MIPI_PHY1_CONTROL,
+                       .enable_map = EXYNOS_MIPI_REGMAP_PMU,
+                       .resetn_val = EXYNOS5_MIPI_PHY_M_RESETN,
+                       .resetn_reg = EXYNOS5420_MIPI_PHY1_CONTROL,
+                       .resetn_map = EXYNOS_MIPI_REGMAP_PMU,
+               }, {
+                       /* EXYNOS_MIPI_PHY_ID_CSIS2 */
+                       .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE,
+                       .enable_val = EXYNOS5_PHY_ENABLE,
+                       .enable_reg = EXYNOS5420_MIPI_PHY2_CONTROL,
+                       .enable_map = EXYNOS_MIPI_REGMAP_PMU,
+                       .resetn_val = EXYNOS5_MIPI_PHY_S_RESETN,
+                       .resetn_reg = EXYNOS5420_MIPI_PHY2_CONTROL,
+                       .resetn_map = EXYNOS_MIPI_REGMAP_PMU,
+               },
+       },
+};
+
+#define EXYNOS5433_SYSREG_DISP_MIPI_PHY                0x100C
+#define EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON   0x1014
+#define EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON   0x1020
+
+static const struct mipi_phy_device_desc exynos5433_mipi_phy = {
+       .num_regmaps = 4,
+       .regmap_names = {
+               "samsung,pmu-syscon",
+               "samsung,disp-sysreg",
+               "samsung,cam0-sysreg",
+               "samsung,cam1-sysreg"
+       },
+       .num_phys = 5,
+       .phys = {
+               {
+                       /* EXYNOS_MIPI_PHY_ID_CSIS0 */
+                       .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0,
+                       .enable_val = EXYNOS5_PHY_ENABLE,
+                       .enable_reg = EXYNOS5433_MIPI_PHY0_CONTROL,
+                       .enable_map = EXYNOS_MIPI_REGMAP_PMU,
+                       .resetn_val = BIT(0),
+                       .resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON,
+                       .resetn_map = EXYNOS_MIPI_REGMAP_CAM0,
+               }, {
+                       /* EXYNOS_MIPI_PHY_ID_DSIM0 */
+                       .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0,
+                       .enable_val = EXYNOS5_PHY_ENABLE,
+                       .enable_reg = EXYNOS5433_MIPI_PHY0_CONTROL,
+                       .enable_map = EXYNOS_MIPI_REGMAP_PMU,
+                       .resetn_val = BIT(0),
+                       .resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY,
+                       .resetn_map = EXYNOS_MIPI_REGMAP_DISP,
+               }, {
+                       /* EXYNOS_MIPI_PHY_ID_CSIS1 */
+                       .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE,
+                       .enable_val = EXYNOS5_PHY_ENABLE,
+                       .enable_reg = EXYNOS5433_MIPI_PHY1_CONTROL,
+                       .enable_map = EXYNOS_MIPI_REGMAP_PMU,
+                       .resetn_val = BIT(1),
+                       .resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON,
+                       .resetn_map = EXYNOS_MIPI_REGMAP_CAM0,
+               }, {
+                       /* EXYNOS_MIPI_PHY_ID_DSIM1 */
+                       .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE,
+                       .enable_val = EXYNOS5_PHY_ENABLE,
+                       .enable_reg = EXYNOS5433_MIPI_PHY1_CONTROL,
+                       .enable_map = EXYNOS_MIPI_REGMAP_PMU,
+                       .resetn_val = BIT(1),
+                       .resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY,
+                       .resetn_map = EXYNOS_MIPI_REGMAP_DISP,
+               }, {
+                       /* EXYNOS_MIPI_PHY_ID_CSIS2 */
+                       .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE,
+                       .enable_val = EXYNOS5_PHY_ENABLE,
+                       .enable_reg = EXYNOS5433_MIPI_PHY2_CONTROL,
+                       .enable_map = EXYNOS_MIPI_REGMAP_PMU,
+                       .resetn_val = BIT(0),
+                       .resetn_reg = EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON,
+                       .resetn_map = EXYNOS_MIPI_REGMAP_CAM1,
+               },
+       },
+};
 
 struct exynos_mipi_video_phy {
+       struct regmap *regmaps[EXYNOS_MIPI_REGMAPS_NUM];
+       int num_phys;
        struct video_phy_desc {
                struct phy *phy;
                unsigned int index;
+               const struct exynos_mipi_phy_desc *data;
        } phys[EXYNOS_MIPI_PHYS_NUM];
        spinlock_t slock;
-       void __iomem *regs;
-       struct regmap *regmap;
 };
 
-static int __set_phy_state(struct exynos_mipi_video_phy *state,
-                       enum exynos_mipi_phy_id id, unsigned int on)
+static inline int __is_running(const struct exynos_mipi_phy_desc *data,
+                       struct exynos_mipi_video_phy *state)
 {
-       const unsigned int offset = EXYNOS4_MIPI_PHY_CONTROL(id / 2);
-       void __iomem *addr;
-       u32 val, reset;
+       u32 val;
 
-       if (is_mipi_dsim_phy_id(id))
-               reset = EXYNOS4_MIPI_PHY_MRESETN;
-       else
-               reset = EXYNOS4_MIPI_PHY_SRESETN;
+       regmap_read(state->regmaps[data->resetn_map], data->resetn_reg, &val);
+       return val & data->resetn_val;
+}
+
+static int __set_phy_state(const struct exynos_mipi_phy_desc *data,
+                          struct exynos_mipi_video_phy *state, unsigned int on)
+{
+       u32 val;
 
        spin_lock(&state->slock);
 
-       if (!IS_ERR(state->regmap)) {
-               regmap_read(state->regmap, offset, &val);
-               if (on)
-                       val |= reset;
-               else
-                       val &= ~reset;
-               regmap_write(state->regmap, offset, val);
-               if (on)
-                       val |= EXYNOS4_MIPI_PHY_ENABLE;
-               else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK))
-                       val &= ~EXYNOS4_MIPI_PHY_ENABLE;
-               regmap_write(state->regmap, offset, val);
-       } else {
-               addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2);
-
-               val = readl(addr);
-               if (on)
-                       val |= reset;
-               else
-                       val &= ~reset;
-               writel(val, addr);
-               /* Clear ENABLE bit only if MRESETN, SRESETN bits are not set */
-               if (on)
-                       val |= EXYNOS4_MIPI_PHY_ENABLE;
-               else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK))
-                       val &= ~EXYNOS4_MIPI_PHY_ENABLE;
-
-               writel(val, addr);
+       /* disable in PMU sysreg */
+       if (!on && data->coupled_phy_id >= 0 &&
+           !__is_running(state->phys[data->coupled_phy_id].data, state)) {
+               regmap_read(state->regmaps[data->enable_map], data->enable_reg,
+                           &val);
+               val &= ~data->enable_val;
+               regmap_write(state->regmaps[data->enable_map], data->enable_reg,
+                            val);
+       }
+
+       /* PHY reset */
+       regmap_read(state->regmaps[data->resetn_map], data->resetn_reg, &val);
+       val = on ? (val | data->resetn_val) : (val & ~data->resetn_val);
+       regmap_write(state->regmaps[data->resetn_map], data->resetn_reg, val);
+
+       /* enable in PMU sysreg */
+       if (on) {
+               regmap_read(state->regmaps[data->enable_map], data->enable_reg,
+                           &val);
+               val |= data->enable_val;
+               regmap_write(state->regmaps[data->enable_map], data->enable_reg,
+                            val);
        }
 
        spin_unlock(&state->slock);
+
        return 0;
 }
 
 #define to_mipi_video_phy(desc) \
-       container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index]);
+       container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index])
 
 static int exynos_mipi_video_phy_power_on(struct phy *phy)
 {
        struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
        struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
 
-       return __set_phy_state(state, phy_desc->index, 1);
+       return __set_phy_state(phy_desc->data, state, 1);
 }
 
 static int exynos_mipi_video_phy_power_off(struct phy *phy)
@@ -110,7 +290,7 @@ static int exynos_mipi_video_phy_power_off(struct phy *phy)
        struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
        struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
 
-       return __set_phy_state(state, phy_desc->index, 0);
+       return __set_phy_state(phy_desc->data, state, 0);
 }
 
 static struct phy *exynos_mipi_video_phy_xlate(struct device *dev,
@@ -118,7 +298,7 @@ static struct phy *exynos_mipi_video_phy_xlate(struct device *dev,
 {
        struct exynos_mipi_video_phy *state = dev_get_drvdata(dev);
 
-       if (WARN_ON(args->args[0] >= EXYNOS_MIPI_PHYS_NUM))
+       if (WARN_ON(args->args[0] >= state->num_phys))
                return ERR_PTR(-ENODEV);
 
        return state->phys[args->args[0]].phy;
@@ -132,32 +312,33 @@ static const struct phy_ops exynos_mipi_video_phy_ops = {
 
 static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
 {
+       const struct mipi_phy_device_desc *phy_dev;
        struct exynos_mipi_video_phy *state;
        struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
        struct phy_provider *phy_provider;
        unsigned int i;
 
+       phy_dev = of_device_get_match_data(dev);
+       if (!phy_dev)
+               return -ENODEV;
+
        state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
        if (!state)
                return -ENOMEM;
 
-       state->regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon");
-       if (IS_ERR(state->regmap)) {
-               struct resource *res;
-
-               dev_info(dev, "regmap lookup failed: %ld\n",
-                        PTR_ERR(state->regmap));
-
-               res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-               state->regs = devm_ioremap_resource(dev, res);
-               if (IS_ERR(state->regs))
-                       return PTR_ERR(state->regs);
+       for (i = 0; i < phy_dev->num_regmaps; i++) {
+               state->regmaps[i] = syscon_regmap_lookup_by_phandle(np,
+                                               phy_dev->regmap_names[i]);
+               if (IS_ERR(state->regmaps[i]))
+                       return PTR_ERR(state->regmaps[i]);
        }
+       state->num_phys = phy_dev->num_phys;
+       spin_lock_init(&state->slock);
 
        dev_set_drvdata(dev, state);
-       spin_lock_init(&state->slock);
 
-       for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
+       for (i = 0; i < state->num_phys; i++) {
                struct phy *phy = devm_phy_create(dev, NULL,
                                                  &exynos_mipi_video_phy_ops);
                if (IS_ERR(phy)) {
@@ -167,6 +348,7 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
 
                state->phys[i].phy = phy;
                state->phys[i].index = i;
+               state->phys[i].data = &phy_dev->phys[i];
                phy_set_drvdata(phy, &state->phys[i]);
        }
 
@@ -177,8 +359,17 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
 }
 
 static const struct of_device_id exynos_mipi_video_phy_of_match[] = {
-       { .compatible = "samsung,s5pv210-mipi-video-phy" },
-       { },
+       {
+               .compatible = "samsung,s5pv210-mipi-video-phy",
+               .data = &s5pv210_mipi_phy,
+       }, {
+               .compatible = "samsung,exynos5420-mipi-video-phy",
+               .data = &exynos5420_mipi_phy,
+       }, {
+               .compatible = "samsung,exynos5433-mipi-video-phy",
+               .data = &exynos5433_mipi_phy,
+       },
+       { /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, exynos_mipi_video_phy_of_match);