From mboxrd@z Thu Jan 1 00:00:00 1970 From: mturquette@linaro.org (Mike Turquette) Date: Wed, 26 Mar 2014 19:38:17 -0700 Subject: [PATCH resend] clk: axi-clkgen: Add support for v2 In-Reply-To: <530ED945.2050907@metafoo.de> References: <1392629513-6114-1-git-send-email-lars@metafoo.de> <20140227010421.12081.82707@quantum> <530ED945.2050907@metafoo.de> Message-ID: <20140327023817.5762.48198@quantum> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org Quoting Lars-Peter Clausen (2014-02-26 22:20:53) > On 02/27/2014 02:04 AM, Mike Turquette wrote: > > Quoting Lars-Peter Clausen (2014-02-17 01:31:53) > >> This patch adds support for the new v2 version of the axi-clkgen core. > >> Unfortunately the method of accessing the registers is quite different on v2, > >> while the content still stays largely the same. So the patch adds a small > >> abstraction layer which implements the specific read and write functions for v1 > >> and v2 in callback functions. > > > > Hi, > > > > This patch almost doubles the size of clk-axi-clkgen.c. Should it be a > > separate clock driver? I guess that depends on the relationship between > > "v1" and "v2". Are both of those versions of the clkgen core going into > > production? > > Hi, > > The only thing that is different between the two versions is how the PLL > registers are accessed. The content that is written to those register is a > 100% identical. So splitting it up into two drivers makes no sense, since > you'd have to copy&paste all the application logic. Both versions of the > core can be found in the wild. Hi Lars, I took this into clk-next some time ago but never replied to this thread letting you know. Better late than never :-) Regards, Mike > > - Lars > > > > > Regards, > > Mike > > > >> > >> Signed-off-by: Lars-Peter Clausen > >> --- > >> .../devicetree/bindings/clock/axi-clkgen.txt | 2 +- > >> drivers/clk/clk-axi-clkgen.c | 312 ++++++++++++++++++--- > >> 2 files changed, 270 insertions(+), 44 deletions(-) > >> > >> diff --git a/Documentation/devicetree/bindings/clock/axi-clkgen.txt b/Documentation/devicetree/bindings/clock/axi-clkgen.txt > >> index 028b493..20e1704 100644 > >> --- a/Documentation/devicetree/bindings/clock/axi-clkgen.txt > >> +++ b/Documentation/devicetree/bindings/clock/axi-clkgen.txt > >> @@ -5,7 +5,7 @@ This binding uses the common clock binding[1]. > >> [1] Documentation/devicetree/bindings/clock/clock-bindings.txt > >> > >> Required properties: > >> -- compatible : shall be "adi,axi-clkgen". > >> +- compatible : shall be "adi,axi-clkgen-1.00.a" or "adi,axi-clkgen-2.00.a". > >> - #clock-cells : from common clock binding; Should always be set to 0. > >> - reg : Address and length of the axi-clkgen register set. > >> - clocks : Phandle and clock specifier for the parent clock. > >> diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c > >> index 8137327..1127ee4 100644 > >> --- a/drivers/clk/clk-axi-clkgen.c > >> +++ b/drivers/clk/clk-axi-clkgen.c > >> @@ -17,23 +17,75 @@ > >> #include > >> #include > >> > >> -#define AXI_CLKGEN_REG_UPDATE_ENABLE 0x04 > >> -#define AXI_CLKGEN_REG_CLK_OUT1 0x08 > >> -#define AXI_CLKGEN_REG_CLK_OUT2 0x0c > >> -#define AXI_CLKGEN_REG_CLK_DIV 0x10 > >> -#define AXI_CLKGEN_REG_CLK_FB1 0x14 > >> -#define AXI_CLKGEN_REG_CLK_FB2 0x18 > >> -#define AXI_CLKGEN_REG_LOCK1 0x1c > >> -#define AXI_CLKGEN_REG_LOCK2 0x20 > >> -#define AXI_CLKGEN_REG_LOCK3 0x24 > >> -#define AXI_CLKGEN_REG_FILTER1 0x28 > >> -#define AXI_CLKGEN_REG_FILTER2 0x2c > >> +#define AXI_CLKGEN_V1_REG_UPDATE_ENABLE 0x04 > >> +#define AXI_CLKGEN_V1_REG_CLK_OUT1 0x08 > >> +#define AXI_CLKGEN_V1_REG_CLK_OUT2 0x0c > >> +#define AXI_CLKGEN_V1_REG_CLK_DIV 0x10 > >> +#define AXI_CLKGEN_V1_REG_CLK_FB1 0x14 > >> +#define AXI_CLKGEN_V1_REG_CLK_FB2 0x18 > >> +#define AXI_CLKGEN_V1_REG_LOCK1 0x1c > >> +#define AXI_CLKGEN_V1_REG_LOCK2 0x20 > >> +#define AXI_CLKGEN_V1_REG_LOCK3 0x24 > >> +#define AXI_CLKGEN_V1_REG_FILTER1 0x28 > >> +#define AXI_CLKGEN_V1_REG_FILTER2 0x2c > >> + > >> +#define AXI_CLKGEN_V2_REG_RESET 0x40 > >> +#define AXI_CLKGEN_V2_REG_DRP_CNTRL 0x70 > >> +#define AXI_CLKGEN_V2_REG_DRP_STATUS 0x74 > >> + > >> +#define AXI_CLKGEN_V2_RESET_MMCM_ENABLE BIT(1) > >> +#define AXI_CLKGEN_V2_RESET_ENABLE BIT(0) > >> + > >> +#define AXI_CLKGEN_V2_DRP_CNTRL_SEL BIT(29) > >> +#define AXI_CLKGEN_V2_DRP_CNTRL_READ BIT(28) > >> + > >> +#define AXI_CLKGEN_V2_DRP_STATUS_BUSY BIT(16) > >> + > >> +#define MMCM_REG_CLKOUT0_1 0x08 > >> +#define MMCM_REG_CLKOUT0_2 0x09 > >> +#define MMCM_REG_CLK_FB1 0x14 > >> +#define MMCM_REG_CLK_FB2 0x15 > >> +#define MMCM_REG_CLK_DIV 0x16 > >> +#define MMCM_REG_LOCK1 0x18 > >> +#define MMCM_REG_LOCK2 0x19 > >> +#define MMCM_REG_LOCK3 0x1a > >> +#define MMCM_REG_FILTER1 0x4e > >> +#define MMCM_REG_FILTER2 0x4f > >> + > >> +struct axi_clkgen; > >> + > >> +struct axi_clkgen_mmcm_ops { > >> + void (*enable)(struct axi_clkgen *axi_clkgen, bool enable); > >> + int (*write)(struct axi_clkgen *axi_clkgen, unsigned int reg, > >> + unsigned int val, unsigned int mask); > >> + int (*read)(struct axi_clkgen *axi_clkgen, unsigned int reg, > >> + unsigned int *val); > >> +}; > >> > >> struct axi_clkgen { > >> void __iomem *base; > >> + const struct axi_clkgen_mmcm_ops *mmcm_ops; > >> struct clk_hw clk_hw; > >> }; > >> > >> +static void axi_clkgen_mmcm_enable(struct axi_clkgen *axi_clkgen, > >> + bool enable) > >> +{ > >> + axi_clkgen->mmcm_ops->enable(axi_clkgen, enable); > >> +} > >> + > >> +static int axi_clkgen_mmcm_write(struct axi_clkgen *axi_clkgen, > >> + unsigned int reg, unsigned int val, unsigned int mask) > >> +{ > >> + return axi_clkgen->mmcm_ops->write(axi_clkgen, reg, val, mask); > >> +} > >> + > >> +static int axi_clkgen_mmcm_read(struct axi_clkgen *axi_clkgen, > >> + unsigned int reg, unsigned int *val) > >> +{ > >> + return axi_clkgen->mmcm_ops->read(axi_clkgen, reg, val); > >> +} > >> + > >> static uint32_t axi_clkgen_lookup_filter(unsigned int m) > >> { > >> switch (m) { > >> @@ -156,6 +208,148 @@ static void axi_clkgen_read(struct axi_clkgen *axi_clkgen, > >> *val = readl(axi_clkgen->base + reg); > >> } > >> > >> +static unsigned int axi_clkgen_v1_map_mmcm_reg(unsigned int reg) > >> +{ > >> + switch (reg) { > >> + case MMCM_REG_CLKOUT0_1: > >> + return AXI_CLKGEN_V1_REG_CLK_OUT1; > >> + case MMCM_REG_CLKOUT0_2: > >> + return AXI_CLKGEN_V1_REG_CLK_OUT2; > >> + case MMCM_REG_CLK_FB1: > >> + return AXI_CLKGEN_V1_REG_CLK_FB1; > >> + case MMCM_REG_CLK_FB2: > >> + return AXI_CLKGEN_V1_REG_CLK_FB2; > >> + case MMCM_REG_CLK_DIV: > >> + return AXI_CLKGEN_V1_REG_CLK_DIV; > >> + case MMCM_REG_LOCK1: > >> + return AXI_CLKGEN_V1_REG_LOCK1; > >> + case MMCM_REG_LOCK2: > >> + return AXI_CLKGEN_V1_REG_LOCK2; > >> + case MMCM_REG_LOCK3: > >> + return AXI_CLKGEN_V1_REG_LOCK3; > >> + case MMCM_REG_FILTER1: > >> + return AXI_CLKGEN_V1_REG_FILTER1; > >> + case MMCM_REG_FILTER2: > >> + return AXI_CLKGEN_V1_REG_FILTER2; > >> + default: > >> + return 0; > >> + } > >> +} > >> + > >> +static int axi_clkgen_v1_mmcm_write(struct axi_clkgen *axi_clkgen, > >> + unsigned int reg, unsigned int val, unsigned int mask) > >> +{ > >> + reg = axi_clkgen_v1_map_mmcm_reg(reg); > >> + if (reg == 0) > >> + return -EINVAL; > >> + > >> + axi_clkgen_write(axi_clkgen, reg, val); > >> + > >> + return 0; > >> +} > >> + > >> +static int axi_clkgen_v1_mmcm_read(struct axi_clkgen *axi_clkgen, > >> + unsigned int reg, unsigned int *val) > >> +{ > >> + reg = axi_clkgen_v1_map_mmcm_reg(reg); > >> + if (reg == 0) > >> + return -EINVAL; > >> + > >> + axi_clkgen_read(axi_clkgen, reg, val); > >> + > >> + return 0; > >> +} > >> + > >> +static void axi_clkgen_v1_mmcm_enable(struct axi_clkgen *axi_clkgen, > >> + bool enable) > >> +{ > >> + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V1_REG_UPDATE_ENABLE, enable); > >> +} > >> + > >> +static const struct axi_clkgen_mmcm_ops axi_clkgen_v1_mmcm_ops = { > >> + .write = axi_clkgen_v1_mmcm_write, > >> + .read = axi_clkgen_v1_mmcm_read, > >> + .enable = axi_clkgen_v1_mmcm_enable, > >> +}; > >> + > >> +static int axi_clkgen_wait_non_busy(struct axi_clkgen *axi_clkgen) > >> +{ > >> + unsigned int timeout = 10000; > >> + unsigned int val; > >> + > >> + do { > >> + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_STATUS, &val); > >> + } while ((val & AXI_CLKGEN_V2_DRP_STATUS_BUSY) && --timeout); > >> + > >> + if (val & AXI_CLKGEN_V2_DRP_STATUS_BUSY) > >> + return -EIO; > >> + > >> + return val & 0xffff; > >> +} > >> + > >> +static int axi_clkgen_v2_mmcm_read(struct axi_clkgen *axi_clkgen, > >> + unsigned int reg, unsigned int *val) > >> +{ > >> + unsigned int reg_val; > >> + int ret; > >> + > >> + ret = axi_clkgen_wait_non_busy(axi_clkgen); > >> + if (ret < 0) > >> + return ret; > >> + > >> + reg_val = AXI_CLKGEN_V2_DRP_CNTRL_SEL | AXI_CLKGEN_V2_DRP_CNTRL_READ; > >> + reg_val |= (reg << 16); > >> + > >> + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val); > >> + > >> + ret = axi_clkgen_wait_non_busy(axi_clkgen); > >> + if (ret < 0) > >> + return ret; > >> + > >> + *val = ret; > >> + > >> + return 0; > >> +} > >> + > >> +static int axi_clkgen_v2_mmcm_write(struct axi_clkgen *axi_clkgen, > >> + unsigned int reg, unsigned int val, unsigned int mask) > >> +{ > >> + unsigned int reg_val = 0; > >> + int ret; > >> + > >> + ret = axi_clkgen_wait_non_busy(axi_clkgen); > >> + if (ret < 0) > >> + return ret; > >> + > >> + if (mask != 0xffff) { > >> + axi_clkgen_v2_mmcm_read(axi_clkgen, reg, ®_val); > >> + reg_val &= ~mask; > >> + } > >> + > >> + reg_val |= AXI_CLKGEN_V2_DRP_CNTRL_SEL | (reg << 16) | (val & mask); > >> + > >> + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val); > >> + > >> + return 0; > >> +} > >> + > >> +static void axi_clkgen_v2_mmcm_enable(struct axi_clkgen *axi_clkgen, > >> + bool enable) > >> +{ > >> + unsigned int val = AXI_CLKGEN_V2_RESET_ENABLE; > >> + > >> + if (enable) > >> + val |= AXI_CLKGEN_V2_RESET_MMCM_ENABLE; > >> + > >> + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_RESET, val); > >> +} > >> + > >> +static const struct axi_clkgen_mmcm_ops axi_clkgen_v2_mmcm_ops = { > >> + .write = axi_clkgen_v2_mmcm_write, > >> + .read = axi_clkgen_v2_mmcm_read, > >> + .enable = axi_clkgen_v2_mmcm_enable, > >> +}; > >> + > >> static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw) > >> { > >> return container_of(clk_hw, struct axi_clkgen, clk_hw); > >> @@ -184,33 +378,29 @@ static int axi_clkgen_set_rate(struct clk_hw *clk_hw, > >> filter = axi_clkgen_lookup_filter(m - 1); > >> lock = axi_clkgen_lookup_lock(m - 1); > >> > >> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 0); > >> - > >> axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount); > >> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, > >> - (high << 6) | low); > >> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT2, > >> - (edge << 7) | (nocount << 6)); > >> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_1, > >> + (high << 6) | low, 0xefff); > >> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_2, > >> + (edge << 7) | (nocount << 6), 0x03ff); > >> > >> axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount); > >> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, > >> - (edge << 13) | (nocount << 12) | (high << 6) | low); > >> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_DIV, > >> + (edge << 13) | (nocount << 12) | (high << 6) | low, 0x3fff); > >> > >> axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount); > >> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, > >> - (high << 6) | low); > >> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB2, > >> - (edge << 7) | (nocount << 6)); > >> - > >> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK1, lock & 0x3ff); > >> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK2, > >> - (((lock >> 16) & 0x1f) << 10) | 0x1); > >> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK3, > >> - (((lock >> 24) & 0x1f) << 10) | 0x3e9); > >> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER1, filter >> 16); > >> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER2, filter); > >> - > >> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 1); > >> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB1, > >> + (high << 6) | low, 0xefff); > >> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB2, > >> + (edge << 7) | (nocount << 6), 0x03ff); > >> + > >> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK1, lock & 0x3ff, 0x3ff); > >> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK2, > >> + (((lock >> 16) & 0x1f) << 10) | 0x1, 0x7fff); > >> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK3, > >> + (((lock >> 24) & 0x1f) << 10) | 0x3e9, 0x7fff); > >> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER1, filter >> 16, 0x9900); > >> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER2, filter, 0x9900); > >> > >> return 0; > >> } > >> @@ -236,11 +426,11 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, > >> unsigned int reg; > >> unsigned long long tmp; > >> > >> - axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, ®); > >> + axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, ®); > >> dout = (reg & 0x3f) + ((reg >> 6) & 0x3f); > >> - axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, ®); > >> + axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, ®); > >> d = (reg & 0x3f) + ((reg >> 6) & 0x3f); > >> - axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, ®); > >> + axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, ®); > >> m = (reg & 0x3f) + ((reg >> 6) & 0x3f); > >> > >> if (d == 0 || dout == 0) > >> @@ -255,14 +445,45 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, > >> return tmp; > >> } > >> > >> +static int axi_clkgen_enable(struct clk_hw *clk_hw) > >> +{ > >> + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); > >> + > >> + axi_clkgen_mmcm_enable(axi_clkgen, true); > >> + > >> + return 0; > >> +} > >> + > >> +static void axi_clkgen_disable(struct clk_hw *clk_hw) > >> +{ > >> + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); > >> + > >> + axi_clkgen_mmcm_enable(axi_clkgen, false); > >> +} > >> + > >> static const struct clk_ops axi_clkgen_ops = { > >> .recalc_rate = axi_clkgen_recalc_rate, > >> .round_rate = axi_clkgen_round_rate, > >> .set_rate = axi_clkgen_set_rate, > >> + .enable = axi_clkgen_enable, > >> + .disable = axi_clkgen_disable, > >> }; > >> > >> +static const struct of_device_id axi_clkgen_ids[] = { > >> + { > >> + .compatible = "adi,axi-clkgen-1.00.a", > >> + .data = &axi_clkgen_v1_mmcm_ops > >> + }, { > >> + .compatible = "adi,axi-clkgen-2.00.a", > >> + .data = &axi_clkgen_v2_mmcm_ops, > >> + }, > >> + { }, > >> +}; > >> +MODULE_DEVICE_TABLE(of, axi_clkgen_ids); > >> + > >> static int axi_clkgen_probe(struct platform_device *pdev) > >> { > >> + const struct of_device_id *id; > >> struct axi_clkgen *axi_clkgen; > >> struct clk_init_data init; > >> const char *parent_name; > >> @@ -270,10 +491,19 @@ static int axi_clkgen_probe(struct platform_device *pdev) > >> struct resource *mem; > >> struct clk *clk; > >> > >> + if (!pdev->dev.of_node) > >> + return -ENODEV; > >> + > >> + id = of_match_node(axi_clkgen_ids, pdev->dev.of_node); > >> + if (!id) > >> + return -ENODEV; > >> + > >> axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL); > >> if (!axi_clkgen) > >> return -ENOMEM; > >> > >> + axi_clkgen->mmcm_ops = id->data; > >> + > >> mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); > >> axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem); > >> if (IS_ERR(axi_clkgen->base)) > >> @@ -289,10 +519,12 @@ static int axi_clkgen_probe(struct platform_device *pdev) > >> > >> init.name = clk_name; > >> init.ops = &axi_clkgen_ops; > >> - init.flags = 0; > >> + init.flags = CLK_SET_RATE_GATE; > >> init.parent_names = &parent_name; > >> init.num_parents = 1; > >> > >> + axi_clkgen_mmcm_enable(axi_clkgen, false); > >> + > >> axi_clkgen->clk_hw.init = &init; > >> clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw); > >> if (IS_ERR(clk)) > >> @@ -309,12 +541,6 @@ static int axi_clkgen_remove(struct platform_device *pdev) > >> return 0; > >> } > >> > >> -static const struct of_device_id axi_clkgen_ids[] = { > >> - { .compatible = "adi,axi-clkgen-1.00.a" }, > >> - { }, > >> -}; > >> -MODULE_DEVICE_TABLE(of, axi_clkgen_ids); > >> - > >> static struct platform_driver axi_clkgen_driver = { > >> .driver = { > >> .name = "adi-axi-clkgen", > >> -- > >> 1.8.0 > >> >