]> rtime.felk.cvut.cz Git - linux-imx.git/blobdiff - drivers/acpi/acpi_lpss.c
Merge branch 'acpi-lpss'
[linux-imx.git] / drivers / acpi / acpi_lpss.c
index 652fd5ce303c4a9efdbfa3f6d4eb330fba42a5bd..6a382188fa20a7f2f67f8f45d506439f8e45db7b 100644 (file)
@@ -32,12 +32,26 @@ ACPI_MODULE_NAME("acpi_lpss");
 #define LPSS_GENERAL_LTR_MODE_SW       BIT(2)
 #define LPSS_SW_LTR                    0x10
 #define LPSS_AUTO_LTR                  0x14
+#define LPSS_TX_INT                    0x20
+#define LPSS_TX_INT_MASK               BIT(1)
+
+struct lpss_shared_clock {
+       const char *name;
+       unsigned long rate;
+       struct clk *clk;
+};
+
+struct lpss_private_data;
 
 struct lpss_device_desc {
        bool clk_required;
        const char *clkdev_name;
        bool ltr_required;
        unsigned int prv_offset;
+       size_t prv_size_override;
+       bool clk_gate;
+       struct lpss_shared_clock *shared_clock;
+       void (*setup)(struct lpss_private_data *pdata);
 };
 
 static struct lpss_device_desc lpss_dma_desc = {
@@ -52,17 +66,76 @@ struct lpss_private_data {
        const struct lpss_device_desc *dev_desc;
 };
 
+static void lpss_uart_setup(struct lpss_private_data *pdata)
+{
+       unsigned int tx_int_offset = pdata->dev_desc->prv_offset + LPSS_TX_INT;
+       u32 reg;
+
+       reg = readl(pdata->mmio_base + tx_int_offset);
+       writel(reg | LPSS_TX_INT_MASK, pdata->mmio_base + tx_int_offset);
+}
+
 static struct lpss_device_desc lpt_dev_desc = {
        .clk_required = true,
        .prv_offset = 0x800,
        .ltr_required = true,
+       .clk_gate = true,
+};
+
+static struct lpss_device_desc lpt_uart_dev_desc = {
+       .clk_required = true,
+       .prv_offset = 0x800,
+       .ltr_required = true,
+       .clk_gate = true,
+       .setup = lpss_uart_setup,
 };
 
 static struct lpss_device_desc lpt_sdio_dev_desc = {
        .prv_offset = 0x1000,
+       .prv_size_override = 0x1018,
        .ltr_required = true,
 };
 
+static struct lpss_shared_clock uart_clock = {
+       .name = "uart_clk",
+       .rate = 44236800,
+};
+
+static struct lpss_device_desc byt_uart_dev_desc = {
+       .clk_required = true,
+       .prv_offset = 0x800,
+       .clk_gate = true,
+       .shared_clock = &uart_clock,
+       .setup = lpss_uart_setup,
+};
+
+static struct lpss_shared_clock spi_clock = {
+       .name = "spi_clk",
+       .rate = 50000000,
+};
+
+static struct lpss_device_desc byt_spi_dev_desc = {
+       .clk_required = true,
+       .prv_offset = 0x400,
+       .clk_gate = true,
+       .shared_clock = &spi_clock,
+};
+
+static struct lpss_device_desc byt_sdio_dev_desc = {
+       .clk_required = true,
+};
+
+static struct lpss_shared_clock i2c_clock = {
+       .name = "i2c_clk",
+       .rate = 100000000,
+};
+
+static struct lpss_device_desc byt_i2c_dev_desc = {
+       .clk_required = true,
+       .prv_offset = 0x800,
+       .shared_clock = &i2c_clock,
+};
+
 static const struct acpi_device_id acpi_lpss_device_ids[] = {
        /* Generic LPSS devices */
        { "INTL9C60", (unsigned long)&lpss_dma_desc },
@@ -72,11 +145,18 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = {
        { "INT33C1", (unsigned long)&lpt_dev_desc },
        { "INT33C2", (unsigned long)&lpt_dev_desc },
        { "INT33C3", (unsigned long)&lpt_dev_desc },
-       { "INT33C4", (unsigned long)&lpt_dev_desc },
-       { "INT33C5", (unsigned long)&lpt_dev_desc },
+       { "INT33C4", (unsigned long)&lpt_uart_dev_desc },
+       { "INT33C5", (unsigned long)&lpt_uart_dev_desc },
        { "INT33C6", (unsigned long)&lpt_sdio_dev_desc },
        { "INT33C7", },
 
+       /* BayTrail LPSS devices */
+       { "80860F0A", (unsigned long)&byt_uart_dev_desc },
+       { "80860F0E", (unsigned long)&byt_spi_dev_desc },
+       { "80860F14", (unsigned long)&byt_sdio_dev_desc },
+       { "80860F41", (unsigned long)&byt_i2c_dev_desc },
+       { "INT33B2", },
+
        { }
 };
 
@@ -98,7 +178,10 @@ static int register_device_clock(struct acpi_device *adev,
                                 struct lpss_private_data *pdata)
 {
        const struct lpss_device_desc *dev_desc = pdata->dev_desc;
+       struct lpss_shared_clock *shared_clock = dev_desc->shared_clock;
+       struct clk *clk = ERR_PTR(-ENODEV);
        struct lpss_clk_data *clk_data;
+       const char *parent;
 
        if (!lpss_clk_dev)
                lpt_register_clock_device();
@@ -117,14 +200,30 @@ static int register_device_clock(struct acpi_device *adev,
            || pdata->mmio_size < dev_desc->prv_offset + LPSS_CLK_SIZE)
                return -ENODATA;
 
-       pdata->clk = clk_register_gate(NULL, dev_name(&adev->dev),
-                                      clk_data->name, 0,
-                                      pdata->mmio_base + dev_desc->prv_offset,
-                                      0, 0, NULL);
-       if (IS_ERR(pdata->clk))
-               return PTR_ERR(pdata->clk);
+       parent = clk_data->name;
+
+       if (shared_clock) {
+               clk = shared_clock->clk;
+               if (!clk) {
+                       clk = clk_register_fixed_rate(NULL, shared_clock->name,
+                                                     "lpss_clk", 0,
+                                                     shared_clock->rate);
+                       shared_clock->clk = clk;
+               }
+               parent = shared_clock->name;
+       }
+
+       if (dev_desc->clk_gate) {
+               clk = clk_register_gate(NULL, dev_name(&adev->dev), parent, 0,
+                                       pdata->mmio_base + dev_desc->prv_offset,
+                                       0, 0, NULL);
+               pdata->clk = clk;
+       }
 
-       clk_register_clkdev(pdata->clk, NULL, dev_name(&adev->dev));
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       clk_register_clkdev(clk, NULL, dev_name(&adev->dev));
        return 0;
 }
 
@@ -152,7 +251,10 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
 
        list_for_each_entry(rentry, &resource_list, node)
                if (resource_type(&rentry->res) == IORESOURCE_MEM) {
-                       pdata->mmio_size = resource_size(&rentry->res);
+                       if (dev_desc->prv_size_override)
+                               pdata->mmio_size = dev_desc->prv_size_override;
+                       else
+                               pdata->mmio_size = resource_size(&rentry->res);
                        pdata->mmio_base = ioremap(rentry->res.start,
                                                   pdata->mmio_size);
                        pdata->dev_desc = dev_desc;
@@ -164,15 +266,27 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
        if (dev_desc->clk_required) {
                ret = register_device_clock(adev, pdata);
                if (ret) {
-                       /*
-                        * Skip the device, but don't terminate the namespace
-                        * scan.
-                        */
-                       kfree(pdata);
-                       return 0;
+                       /* Skip the device, but continue the namespace scan. */
+                       ret = 0;
+                       goto err_out;
                }
        }
 
+       /*
+        * This works around a known issue in ACPI tables where LPSS devices
+        * have _PS0 and _PS3 without _PSC (and no power resources), so
+        * acpi_bus_init_power() will assume that the BIOS has put them into D0.
+        */
+       ret = acpi_device_fix_up_power(adev);
+       if (ret) {
+               /* Skip the device, but continue the namespace scan. */
+               ret = 0;
+               goto err_out;
+       }
+
+       if (dev_desc->setup)
+               dev_desc->setup(pdata);
+
        adev->driver_data = pdata;
        ret = acpi_create_platform_device(adev, id);
        if (ret > 0)