lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
Date: Thu, 27 Feb 2014 07:20:53 +0100 From: Lars-Peter Clausen <lars@...afoo.de> To: Mike Turquette <mturquette@...aro.org> CC: linux-arm-kernel@...ts.infradead.org, linux-kernel@...r.kernel.org Subject: Re: [PATCH resend] clk: axi-clkgen: Add support for v2 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. - Lars > > Regards, > Mike > >> >> Signed-off-by: Lars-Peter Clausen <lars@...afoo.de> >> --- >> .../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 <linux/module.h> >> #include <linux/err.h> >> >> -#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 >> -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@...r.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists