ACPI / LPSS: support for fractional divider clock
authorHeikki Krogerus <heikki.krogerus@linux.intel.com>
Mon, 19 May 2014 11:42:07 +0000 (14:42 +0300)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Sun, 25 May 2014 12:40:57 +0000 (14:40 +0200)
This creates fractional divider type clock for the ones that
have it. It is needed by the UART driver as the clock rate must
accommodate to the requested baud rate.

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/acpi_lpss.c

index a01d4d1343ddc44ac1a31192599d023cd447a4ac..d1c9b04e29a3f93953cc981427dc0c7878785c91 100644 (file)
@@ -29,6 +29,7 @@ ACPI_MODULE_NAME("acpi_lpss");
 #define LPSS_LTR_SIZE  0x18
 
 /* Offsets relative to LPSS_PRIVATE_OFFSET */
+#define LPSS_CLK_DIVIDER_DEF_MASK      (BIT(1) | BIT(16))
 #define LPSS_GENERAL                   0x08
 #define LPSS_GENERAL_LTR_MODE_SW       BIT(2)
 #define LPSS_GENERAL_UART_RTS_OVRD     BIT(3)
@@ -60,6 +61,7 @@ struct lpss_device_desc {
        bool ltr_required;
        unsigned int prv_offset;
        size_t prv_size_override;
+       bool clk_divider;
        bool clk_gate;
        bool save_ctx;
        struct lpss_shared_clock *shared_clock;
@@ -94,6 +96,14 @@ static void lpss_uart_setup(struct lpss_private_data *pdata)
 }
 
 static struct lpss_device_desc lpt_dev_desc = {
+       .clk_required = true,
+       .prv_offset = 0x800,
+       .ltr_required = true,
+       .clk_divider = true,
+       .clk_gate = true,
+};
+
+static struct lpss_device_desc lpt_i2c_dev_desc = {
        .clk_required = true,
        .prv_offset = 0x800,
        .ltr_required = true,
@@ -104,6 +114,7 @@ static struct lpss_device_desc lpt_uart_dev_desc = {
        .clk_required = true,
        .prv_offset = 0x800,
        .ltr_required = true,
+       .clk_divider = true,
        .clk_gate = true,
        .setup = lpss_uart_setup,
 };
@@ -125,31 +136,21 @@ static struct lpss_device_desc byt_pwm_dev_desc = {
        .shared_clock = &pwm_clock,
 };
 
-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_divider = true,
        .clk_gate = true,
        .save_ctx = 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_divider = true,
        .clk_gate = true,
        .save_ctx = true,
-       .shared_clock = &spi_clock,
 };
 
 static struct lpss_device_desc byt_sdio_dev_desc = {
@@ -175,8 +176,8 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = {
        /* Lynxpoint LPSS devices */
        { "INT33C0", (unsigned long)&lpt_dev_desc },
        { "INT33C1", (unsigned long)&lpt_dev_desc },
-       { "INT33C2", (unsigned long)&lpt_dev_desc },
-       { "INT33C3", (unsigned long)&lpt_dev_desc },
+       { "INT33C2", (unsigned long)&lpt_i2c_dev_desc },
+       { "INT33C3", (unsigned long)&lpt_i2c_dev_desc },
        { "INT33C4", (unsigned long)&lpt_uart_dev_desc },
        { "INT33C5", (unsigned long)&lpt_uart_dev_desc },
        { "INT33C6", (unsigned long)&lpt_sdio_dev_desc },
@@ -192,8 +193,8 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = {
 
        { "INT3430", (unsigned long)&lpt_dev_desc },
        { "INT3431", (unsigned long)&lpt_dev_desc },
-       { "INT3432", (unsigned long)&lpt_dev_desc },
-       { "INT3433", (unsigned long)&lpt_dev_desc },
+       { "INT3432", (unsigned long)&lpt_i2c_dev_desc },
+       { "INT3433", (unsigned long)&lpt_i2c_dev_desc },
        { "INT3434", (unsigned long)&lpt_uart_dev_desc },
        { "INT3435", (unsigned long)&lpt_uart_dev_desc },
        { "INT3436", (unsigned long)&lpt_sdio_dev_desc },
@@ -221,9 +222,11 @@ static int register_device_clock(struct acpi_device *adev,
 {
        const struct lpss_device_desc *dev_desc = pdata->dev_desc;
        struct lpss_shared_clock *shared_clock = dev_desc->shared_clock;
+       const char *devname = dev_name(&adev->dev);
        struct clk *clk = ERR_PTR(-ENODEV);
        struct lpss_clk_data *clk_data;
-       const char *parent;
+       const char *parent, *clk_name;
+       void __iomem *prv_base;
 
        if (!lpss_clk_dev)
                lpt_register_clock_device();
@@ -234,7 +237,7 @@ static int register_device_clock(struct acpi_device *adev,
 
        if (dev_desc->clkdev_name) {
                clk_register_clkdev(clk_data->clk, dev_desc->clkdev_name,
-                                   dev_name(&adev->dev));
+                                   devname);
                return 0;
        }
 
@@ -243,6 +246,7 @@ static int register_device_clock(struct acpi_device *adev,
                return -ENODATA;
 
        parent = clk_data->name;
+       prv_base = pdata->mmio_base + dev_desc->prv_offset;
 
        if (shared_clock) {
                clk = shared_clock->clk;
@@ -256,16 +260,41 @@ static int register_device_clock(struct acpi_device *adev,
        }
 
        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 = clk_register_gate(NULL, devname, parent, 0,
+                                       prv_base, 0, 0, NULL);
+               parent = devname;
+       }
+
+       if (dev_desc->clk_divider) {
+               /* Prevent division by zero */
+               if (!readl(prv_base))
+                       writel(LPSS_CLK_DIVIDER_DEF_MASK, prv_base);
+
+               clk_name = kasprintf(GFP_KERNEL, "%s-div", devname);
+               if (!clk_name)
+                       return -ENOMEM;
+               clk = clk_register_fractional_divider(NULL, clk_name, parent,
+                                                     0, prv_base,
+                                                     1, 15, 16, 15, 0, NULL);
+               parent = clk_name;
+
+               clk_name = kasprintf(GFP_KERNEL, "%s-update", devname);
+               if (!clk_name) {
+                       kfree(parent);
+                       return -ENOMEM;
+               }
+               clk = clk_register_gate(NULL, clk_name, parent,
+                                       CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
+                                       prv_base, 31, 0, NULL);
+               kfree(parent);
+               kfree(clk_name);
        }
 
        if (IS_ERR(clk))
                return PTR_ERR(clk);
 
-       clk_register_clkdev(clk, NULL, dev_name(&adev->dev));
+       pdata->clk = clk;
+       clk_register_clkdev(clk, NULL, devname);
        return 0;
 }