Merge tag 'for-6.8/io_uring-2024-01-18' of git://git.kernel.dk/linux
[linux-block.git] / drivers / gpio / gpio-ixp4xx.c
index dde6cf3a5779b6e53c4e566ededd34604883b5f0..c5a9fa64056648e20a53a646e5e3066a070c2429 100644 (file)
 #define IXP4XX_GPIO_STYLE_MASK         GENMASK(2, 0)
 #define IXP4XX_GPIO_STYLE_SIZE         3
 
+/*
+ * Clock output control register defines.
+ */
+#define IXP4XX_GPCLK_CLK0DC_SHIFT      0
+#define IXP4XX_GPCLK_CLK0TC_SHIFT      4
+#define IXP4XX_GPCLK_CLK0_MASK         GENMASK(7, 0)
+#define IXP4XX_GPCLK_MUX14             BIT(8)
+#define IXP4XX_GPCLK_CLK1DC_SHIFT      16
+#define IXP4XX_GPCLK_CLK1TC_SHIFT      20
+#define IXP4XX_GPCLK_CLK1_MASK         GENMASK(23, 16)
+#define IXP4XX_GPCLK_MUX15             BIT(24)
+
 /**
  * struct ixp4xx_gpio - IXP4 GPIO state container
  * @dev: containing device for this instance
@@ -202,6 +214,8 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev)
        struct ixp4xx_gpio *g;
        struct gpio_irq_chip *girq;
        struct device_node *irq_parent;
+       bool clk_14, clk_15;
+       u32 val;
        int ret;
 
        g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL);
@@ -225,13 +239,48 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev)
        }
        g->fwnode = of_node_to_fwnode(np);
 
+       /*
+        * If either clock output is enabled explicitly in the device tree
+        * we take full control of the clock by masking off all bits for
+        * the clock control and selectively enabling them. Otherwise
+        * we leave the hardware default settings.
+        *
+        * Enable clock outputs with default timings of requested clock.
+        * If you need control over TC and DC, add these to the device
+        * tree bindings and use them here.
+        */
+       clk_14 = of_property_read_bool(np, "intel,ixp4xx-gpio14-clkout");
+       clk_15 = of_property_read_bool(np, "intel,ixp4xx-gpio15-clkout");
+
        /*
         * Make sure GPIO 14 and 15 are NOT used as clocks but GPIO on
         * specific machines.
         */
        if (of_machine_is_compatible("dlink,dsm-g600-a") ||
            of_machine_is_compatible("iom,nas-100d"))
-               __raw_writel(0x0, g->base + IXP4XX_REG_GPCLK);
+               val = 0;
+       else {
+               val = __raw_readl(g->base + IXP4XX_REG_GPCLK);
+
+               if (clk_14 || clk_15) {
+                       val &= ~(IXP4XX_GPCLK_MUX14 | IXP4XX_GPCLK_MUX15);
+                       val &= ~IXP4XX_GPCLK_CLK0_MASK;
+                       val &= ~IXP4XX_GPCLK_CLK1_MASK;
+                       if (clk_14) {
+                               /* IXP4XX_GPCLK_CLK0DC implicit low */
+                               val |= (1 << IXP4XX_GPCLK_CLK0TC_SHIFT);
+                               val |= IXP4XX_GPCLK_MUX14;
+                       }
+
+                       if (clk_15) {
+                               /* IXP4XX_GPCLK_CLK1DC implicit low */
+                               val |= (1 << IXP4XX_GPCLK_CLK1TC_SHIFT);
+                               val |= IXP4XX_GPCLK_MUX15;
+                       }
+               }
+       }
+
+       __raw_writel(val, g->base + IXP4XX_REG_GPCLK);
 
        /*
         * This is a very special big-endian ARM issue: when the IXP4xx is