Merge tag 'tegra-for-3.14-powergate' of git://git.kernel.org/pub/scm/linux/kernel...
authorOlof Johansson <olof@lixom.net>
Thu, 26 Dec 2013 18:57:50 +0000 (10:57 -0800)
committerOlof Johansson <olof@lixom.net>
Thu, 26 Dec 2013 18:57:50 +0000 (10:57 -0800)
From Stephen Warren:
ARM: tegra: powergate driver changes

This branch includes all the changes to Tegra's powergate driver for 3.14.
These are separate out, since the Tegra DRM changes for 3.14 rely on the
new APIs introduced here.

A few cleanups and fixes are included, plus additions of Tegra124 SoC
support, and a new API for manipulating Tegra's IO rail deep power down
states.

This branch is based on tag tegra-for-3.14-dmas-resets-rework, in order
to avoid conflicts with the addition of common reset controller support
to the powergate driver.

* tag 'tegra-for-3.14-powergate' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux:
  ARM: tegra: Add IO rail support
  ARM: tegra: Special-case the 3D clamps on Tegra124
  ARM: tegra: Add Tegra124 powergate support
  ARM: tegra: Export tegra_powergate_remove_clamping()
  ARM: tegra: Export tegra_powergate_power_off()
  ARM: tegra: Rename cpu0 powergate to crail
  ARM: tegra: Fix some whitespace oddities

Signed-off-by: Olof Johansson <olof@lixom.net>
arch/arm/mach-tegra/powergate.c
include/linux/tegra-powergate.h

index f6f5b54ff95edcd106ff82b718a251ba99123f31..3d0c537d9b945af08de57ecaa354169674fdc33e 100644 (file)
 #include "fuse.h"
 #include "iomap.h"
 
+#define DPD_SAMPLE             0x020
+#define  DPD_SAMPLE_ENABLE     (1 << 0)
+#define  DPD_SAMPLE_DISABLE    (0 << 0)
+
 #define PWRGATE_TOGGLE         0x30
 #define  PWRGATE_TOGGLE_START  (1 << 8)
 
 
 #define PWRGATE_STATUS         0x38
 
+#define IO_DPD_REQ             0x1b8
+#define  IO_DPD_REQ_CODE_IDLE  (0 << 30)
+#define  IO_DPD_REQ_CODE_OFF   (1 << 30)
+#define  IO_DPD_REQ_CODE_ON    (2 << 30)
+#define  IO_DPD_REQ_CODE_MASK  (3 << 30)
+
+#define IO_DPD_STATUS          0x1bc
+#define IO_DPD2_REQ            0x1c0
+#define IO_DPD2_STATUS         0x1c4
+#define SEL_DPD_TIM            0x1c8
+
+#define GPU_RG_CNTRL           0x2d4
+
 static int tegra_num_powerdomains;
 static int tegra_num_cpu_domains;
 static const u8 *tegra_cpu_domains;
@@ -59,6 +76,13 @@ static const u8 tegra114_cpu_domains[] = {
        TEGRA_POWERGATE_CPU3,
 };
 
+static const u8 tegra124_cpu_domains[] = {
+       TEGRA_POWERGATE_CPU0,
+       TEGRA_POWERGATE_CPU1,
+       TEGRA_POWERGATE_CPU2,
+       TEGRA_POWERGATE_CPU3,
+};
+
 static DEFINE_SPINLOCK(tegra_powergate_lock);
 
 static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
@@ -109,6 +133,7 @@ int tegra_powergate_power_off(int id)
 
        return tegra_powergate_set(id, false);
 }
+EXPORT_SYMBOL(tegra_powergate_power_off);
 
 int tegra_powergate_is_powered(int id)
 {
@@ -128,13 +153,24 @@ int tegra_powergate_remove_clamping(int id)
        if (id < 0 || id >= tegra_num_powerdomains)
                return -EINVAL;
 
+       /*
+        * The Tegra124 GPU has a separate register (with different semantics)
+        * to remove clamps.
+        */
+       if (tegra_chip_id == TEGRA124) {
+               if (id == TEGRA_POWERGATE_3D) {
+                       pmc_write(0, GPU_RG_CNTRL);
+                       return 0;
+               }
+       }
+
        /*
         * Tegra 2 has a bug where PCIE and VDE clamping masks are
         * swapped relatively to the partition ids
         */
-       if (id ==  TEGRA_POWERGATE_VDEC)
+       if (id == TEGRA_POWERGATE_VDEC)
                mask = (1 << TEGRA_POWERGATE_PCIE);
-       else if (id == TEGRA_POWERGATE_PCIE)
+       else if (id == TEGRA_POWERGATE_PCIE)
                mask = (1 << TEGRA_POWERGATE_VDEC);
        else
                mask = (1 << id);
@@ -143,6 +179,7 @@ int tegra_powergate_remove_clamping(int id)
 
        return 0;
 }
+EXPORT_SYMBOL(tegra_powergate_remove_clamping);
 
 /* Must be called with clk disabled, and returns with clk enabled */
 int tegra_powergate_sequence_power_up(int id, struct clk *clk,
@@ -204,6 +241,11 @@ int __init tegra_powergate_init(void)
                tegra_num_cpu_domains = 4;
                tegra_cpu_domains = tegra114_cpu_domains;
                break;
+       case TEGRA124:
+               tegra_num_powerdomains = 25;
+               tegra_num_cpu_domains = 4;
+               tegra_cpu_domains = tegra124_cpu_domains;
+               break;
        default:
                /* Unknown Tegra variant. Disable powergating */
                tegra_num_powerdomains = 0;
@@ -245,12 +287,36 @@ static const char * const powergate_name_t30[] = {
 };
 
 static const char * const powergate_name_t114[] = {
-       [TEGRA_POWERGATE_CPU]   = "cpu0",
+       [TEGRA_POWERGATE_CPU]   = "crail",
+       [TEGRA_POWERGATE_3D]    = "3d",
+       [TEGRA_POWERGATE_VENC]  = "venc",
+       [TEGRA_POWERGATE_VDEC]  = "vdec",
+       [TEGRA_POWERGATE_MPE]   = "mpe",
+       [TEGRA_POWERGATE_HEG]   = "heg",
+       [TEGRA_POWERGATE_CPU1]  = "cpu1",
+       [TEGRA_POWERGATE_CPU2]  = "cpu2",
+       [TEGRA_POWERGATE_CPU3]  = "cpu3",
+       [TEGRA_POWERGATE_CELP]  = "celp",
+       [TEGRA_POWERGATE_CPU0]  = "cpu0",
+       [TEGRA_POWERGATE_C0NC]  = "c0nc",
+       [TEGRA_POWERGATE_C1NC]  = "c1nc",
+       [TEGRA_POWERGATE_DIS]   = "dis",
+       [TEGRA_POWERGATE_DISB]  = "disb",
+       [TEGRA_POWERGATE_XUSBA] = "xusba",
+       [TEGRA_POWERGATE_XUSBB] = "xusbb",
+       [TEGRA_POWERGATE_XUSBC] = "xusbc",
+};
+
+static const char * const powergate_name_t124[] = {
+       [TEGRA_POWERGATE_CPU]   = "crail",
        [TEGRA_POWERGATE_3D]    = "3d",
        [TEGRA_POWERGATE_VENC]  = "venc",
+       [TEGRA_POWERGATE_PCIE]  = "pcie",
        [TEGRA_POWERGATE_VDEC]  = "vdec",
+       [TEGRA_POWERGATE_L2]    = "l2",
        [TEGRA_POWERGATE_MPE]   = "mpe",
        [TEGRA_POWERGATE_HEG]   = "heg",
+       [TEGRA_POWERGATE_SATA]  = "sata",
        [TEGRA_POWERGATE_CPU1]  = "cpu1",
        [TEGRA_POWERGATE_CPU2]  = "cpu2",
        [TEGRA_POWERGATE_CPU3]  = "cpu3",
@@ -258,11 +324,14 @@ static const char * const powergate_name_t114[] = {
        [TEGRA_POWERGATE_CPU0]  = "cpu0",
        [TEGRA_POWERGATE_C0NC]  = "c0nc",
        [TEGRA_POWERGATE_C1NC]  = "c1nc",
+       [TEGRA_POWERGATE_SOR]   = "sor",
        [TEGRA_POWERGATE_DIS]   = "dis",
        [TEGRA_POWERGATE_DISB]  = "disb",
        [TEGRA_POWERGATE_XUSBA] = "xusba",
        [TEGRA_POWERGATE_XUSBB] = "xusbb",
        [TEGRA_POWERGATE_XUSBC] = "xusbc",
+       [TEGRA_POWERGATE_VIC]   = "vic",
+       [TEGRA_POWERGATE_IRAM]  = "iram",
 };
 
 static int powergate_show(struct seq_file *s, void *data)
@@ -309,6 +378,9 @@ int __init tegra_powergate_debugfs_init(void)
        case TEGRA114:
                powergate_name = powergate_name_t114;
                break;
+       case TEGRA124:
+               powergate_name = powergate_name_t124;
+               break;
        }
 
        if (powergate_name) {
@@ -322,3 +394,120 @@ int __init tegra_powergate_debugfs_init(void)
 }
 
 #endif
+
+static int tegra_io_rail_prepare(int id, unsigned long *request,
+                                unsigned long *status, unsigned int *bit)
+{
+       unsigned long rate, value;
+       struct clk *clk;
+
+       *bit = id % 32;
+
+       /*
+        * There are two sets of 30 bits to select IO rails, but bits 30 and
+        * 31 are control bits rather than IO rail selection bits.
+        */
+       if (id > 63 || *bit == 30 || *bit == 31)
+               return -EINVAL;
+
+       if (id < 32) {
+               *status = IO_DPD_STATUS;
+               *request = IO_DPD_REQ;
+       } else {
+               *status = IO_DPD2_STATUS;
+               *request = IO_DPD2_REQ;
+       }
+
+       clk = clk_get_sys(NULL, "pclk");
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       rate = clk_get_rate(clk);
+       clk_put(clk);
+
+       pmc_write(DPD_SAMPLE_ENABLE, DPD_SAMPLE);
+
+       /* must be at least 200 ns, in APB (PCLK) clock cycles */
+       value = DIV_ROUND_UP(1000000000, rate);
+       value = DIV_ROUND_UP(200, value);
+       pmc_write(value, SEL_DPD_TIM);
+
+       return 0;
+}
+
+static int tegra_io_rail_poll(unsigned long offset, unsigned long mask,
+                             unsigned long val, unsigned long timeout)
+{
+       unsigned long value;
+
+       timeout = jiffies + msecs_to_jiffies(timeout);
+
+       while (time_after(timeout, jiffies)) {
+               value = pmc_read(offset);
+               if ((value & mask) == val)
+                       return 0;
+
+               usleep_range(250, 1000);
+       }
+
+       return -ETIMEDOUT;
+}
+
+static void tegra_io_rail_unprepare(void)
+{
+       pmc_write(DPD_SAMPLE_DISABLE, DPD_SAMPLE);
+}
+
+int tegra_io_rail_power_on(int id)
+{
+       unsigned long request, status, value;
+       unsigned int bit, mask;
+       int err;
+
+       err = tegra_io_rail_prepare(id, &request, &status, &bit);
+       if (err < 0)
+               return err;
+
+       mask = 1 << bit;
+
+       value = pmc_read(request);
+       value |= mask;
+       value &= ~IO_DPD_REQ_CODE_MASK;
+       value |= IO_DPD_REQ_CODE_OFF;
+       pmc_write(value, request);
+
+       err = tegra_io_rail_poll(status, mask, 0, 250);
+       if (err < 0)
+               return err;
+
+       tegra_io_rail_unprepare();
+
+       return 0;
+}
+
+int tegra_io_rail_power_off(int id)
+{
+       unsigned long request, status, value;
+       unsigned int bit, mask;
+       int err;
+
+       err = tegra_io_rail_prepare(id, &request, &status, &bit);
+       if (err < 0)
+               return err;
+
+       mask = 1 << bit;
+
+       value = pmc_read(request);
+       value |= mask;
+       value &= ~IO_DPD_REQ_CODE_MASK;
+       value |= IO_DPD_REQ_CODE_ON;
+       pmc_write(value, request);
+
+       err = tegra_io_rail_poll(status, mask, mask, 250);
+       if (err < 0)
+               return err;
+
+       tegra_io_rail_unprepare();
+
+       return 0;
+}
index afe442d2629adcb5841455381fd694414a1809cf..e6f2ab3014a77f80236ccd78ed50249cf20a4d22 100644 (file)
@@ -38,14 +38,49 @@ struct reset_control;
 #define TEGRA_POWERGATE_CPU0   14
 #define TEGRA_POWERGATE_C0NC   15
 #define TEGRA_POWERGATE_C1NC   16
+#define TEGRA_POWERGATE_SOR    17
 #define TEGRA_POWERGATE_DIS    18
 #define TEGRA_POWERGATE_DISB   19
 #define TEGRA_POWERGATE_XUSBA  20
 #define TEGRA_POWERGATE_XUSBB  21
 #define TEGRA_POWERGATE_XUSBC  22
+#define TEGRA_POWERGATE_VIC    23
+#define TEGRA_POWERGATE_IRAM   24
 
 #define TEGRA_POWERGATE_3D0    TEGRA_POWERGATE_3D
 
+#define TEGRA_IO_RAIL_CSIA     0
+#define TEGRA_IO_RAIL_CSIB     1
+#define TEGRA_IO_RAIL_DSI      2
+#define TEGRA_IO_RAIL_MIPI_BIAS        3
+#define TEGRA_IO_RAIL_PEX_BIAS 4
+#define TEGRA_IO_RAIL_PEX_CLK1 5
+#define TEGRA_IO_RAIL_PEX_CLK2 6
+#define TEGRA_IO_RAIL_USB0     9
+#define TEGRA_IO_RAIL_USB1     10
+#define TEGRA_IO_RAIL_USB2     11
+#define TEGRA_IO_RAIL_USB_BIAS 12
+#define TEGRA_IO_RAIL_NAND     13
+#define TEGRA_IO_RAIL_UART     14
+#define TEGRA_IO_RAIL_BB       15
+#define TEGRA_IO_RAIL_AUDIO    17
+#define TEGRA_IO_RAIL_HSIC     19
+#define TEGRA_IO_RAIL_COMP     22
+#define TEGRA_IO_RAIL_HDMI     28
+#define TEGRA_IO_RAIL_PEX_CNTRL        32
+#define TEGRA_IO_RAIL_SDMMC1   33
+#define TEGRA_IO_RAIL_SDMMC3   34
+#define TEGRA_IO_RAIL_SDMMC4   35
+#define TEGRA_IO_RAIL_CAM      36
+#define TEGRA_IO_RAIL_RES      37
+#define TEGRA_IO_RAIL_HV       38
+#define TEGRA_IO_RAIL_DSIB     39
+#define TEGRA_IO_RAIL_DSIC     40
+#define TEGRA_IO_RAIL_DSID     41
+#define TEGRA_IO_RAIL_CSIE     44
+#define TEGRA_IO_RAIL_LVDS     57
+#define TEGRA_IO_RAIL_SYS_DDC  58
+
 #ifdef CONFIG_ARCH_TEGRA
 int tegra_powergate_is_powered(int id);
 int tegra_powergate_power_on(int id);
@@ -55,6 +90,9 @@ int tegra_powergate_remove_clamping(int id);
 /* Must be called with clk disabled, and returns with clk enabled */
 int tegra_powergate_sequence_power_up(int id, struct clk *clk,
                                      struct reset_control *rst);
+
+int tegra_io_rail_power_on(int id);
+int tegra_io_rail_power_off(int id);
 #else
 static inline int tegra_powergate_is_powered(int id)
 {
@@ -81,6 +119,16 @@ static inline int tegra_powergate_sequence_power_up(int id, struct clk *clk,
 {
        return -ENOSYS;
 }
+
+static inline int tegra_io_rail_power_on(int id)
+{
+       return -ENOSYS;
+}
+
+static inline int tegra_io_rail_power_off(int id)
+{
+       return -ENOSYS;
+}
 #endif
 
 #endif /* _MACH_TEGRA_POWERGATE_H_ */