diff options
| author | Boris BREZILLON <b.brezillon@overkiz.com> | 2013-10-11 06:22:06 -0400 |
|---|---|---|
| committer | Nicolas Ferre <nicolas.ferre@atmel.com> | 2013-12-02 09:31:25 -0500 |
| commit | f090fb37def3eb9ea49b71751dbe43f423f3a152 (patch) | |
| tree | 50cb4902013dfa7addaeaba993b37f8af674303b | |
| parent | 1f22f8bb64b3619324020b48b03181914b1f5544 (diff) | |
clk: at91: add PMC utmi clock
This adds new at91 utmi clock implementation using common clk framework.
This clock is a pll with a fixed factor (x40).
It is used as a source for usb clock.
Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Acked-by: Mike Turquette <mturquette@linaro.org>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
| -rw-r--r-- | arch/arm/mach-at91/Kconfig | 7 | ||||
| -rw-r--r-- | drivers/clk/at91/Makefile | 1 | ||||
| -rw-r--r-- | drivers/clk/at91/clk-utmi.c | 159 | ||||
| -rw-r--r-- | drivers/clk/at91/pmc.c | 7 | ||||
| -rw-r--r-- | drivers/clk/at91/pmc.h | 5 |
5 files changed, 179 insertions, 0 deletions
diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index 85b53a441459..6ad37daca971 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig | |||
| @@ -1,5 +1,8 @@ | |||
| 1 | if ARCH_AT91 | 1 | if ARCH_AT91 |
| 2 | 2 | ||
| 3 | config HAVE_AT91_UTMI | ||
| 4 | bool | ||
| 5 | |||
| 3 | config HAVE_AT91_DBGU0 | 6 | config HAVE_AT91_DBGU0 |
| 4 | bool | 7 | bool |
| 5 | 8 | ||
| @@ -78,6 +81,7 @@ config SOC_SAMA5D3 | |||
| 78 | select HAVE_FB_ATMEL | 81 | select HAVE_FB_ATMEL |
| 79 | select HAVE_AT91_DBGU1 | 82 | select HAVE_AT91_DBGU1 |
| 80 | select AT91_USE_OLD_CLK | 83 | select AT91_USE_OLD_CLK |
| 84 | select HAVE_AT91_UTMI | ||
| 81 | help | 85 | help |
| 82 | Select this if you are using one of Atmel's SAMA5D3 family SoC. | 86 | Select this if you are using one of Atmel's SAMA5D3 family SoC. |
| 83 | This support covers SAMA5D31, SAMA5D33, SAMA5D34, SAMA5D35. | 87 | This support covers SAMA5D31, SAMA5D33, SAMA5D34, SAMA5D35. |
| @@ -124,6 +128,7 @@ config SOC_AT91SAM9RL | |||
| 124 | select HAVE_FB_ATMEL | 128 | select HAVE_FB_ATMEL |
| 125 | select SOC_AT91SAM9 | 129 | select SOC_AT91SAM9 |
| 126 | select AT91_USE_OLD_CLK | 130 | select AT91_USE_OLD_CLK |
| 131 | select HAVE_AT91_UTMI | ||
| 127 | 132 | ||
| 128 | config SOC_AT91SAM9G45 | 133 | config SOC_AT91SAM9G45 |
| 129 | bool "AT91SAM9G45 or AT91SAM9M10 families" | 134 | bool "AT91SAM9G45 or AT91SAM9M10 families" |
| @@ -131,6 +136,7 @@ config SOC_AT91SAM9G45 | |||
| 131 | select HAVE_FB_ATMEL | 136 | select HAVE_FB_ATMEL |
| 132 | select SOC_AT91SAM9 | 137 | select SOC_AT91SAM9 |
| 133 | select AT91_USE_OLD_CLK | 138 | select AT91_USE_OLD_CLK |
| 139 | select HAVE_AT91_UTMI | ||
| 134 | help | 140 | help |
| 135 | Select this if you are using one of Atmel's AT91SAM9G45 family SoC. | 141 | Select this if you are using one of Atmel's AT91SAM9G45 family SoC. |
| 136 | This support covers AT91SAM9G45, AT91SAM9G46, AT91SAM9M10 and AT91SAM9M11. | 142 | This support covers AT91SAM9G45, AT91SAM9G46, AT91SAM9M10 and AT91SAM9M11. |
| @@ -141,6 +147,7 @@ config SOC_AT91SAM9X5 | |||
| 141 | select HAVE_FB_ATMEL | 147 | select HAVE_FB_ATMEL |
| 142 | select SOC_AT91SAM9 | 148 | select SOC_AT91SAM9 |
| 143 | select AT91_USE_OLD_CLK | 149 | select AT91_USE_OLD_CLK |
| 150 | select HAVE_AT91_UTMI | ||
| 144 | help | 151 | help |
| 145 | Select this if you are using one of Atmel's AT91SAM9x5 family SoC. | 152 | Select this if you are using one of Atmel's AT91SAM9x5 family SoC. |
| 146 | This means that your SAM9 name finishes with a '5' (except if it is | 153 | This means that your SAM9 name finishes with a '5' (except if it is |
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 3873b621768f..a82488323ea4 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile | |||
| @@ -7,3 +7,4 @@ obj-y += clk-main.o clk-pll.o clk-plldiv.o clk-master.o | |||
| 7 | obj-y += clk-system.o clk-peripheral.o | 7 | obj-y += clk-system.o clk-peripheral.o |
| 8 | 8 | ||
| 9 | obj-$(CONFIG_AT91_PROGRAMMABLE_CLOCKS) += clk-programmable.o | 9 | obj-$(CONFIG_AT91_PROGRAMMABLE_CLOCKS) += clk-programmable.o |
| 10 | obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o | ||
diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c new file mode 100644 index 000000000000..ae3263bc1476 --- /dev/null +++ b/drivers/clk/at91/clk-utmi.c | |||
| @@ -0,0 +1,159 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License as published by | ||
| 6 | * the Free Software Foundation; either version 2 of the License, or | ||
| 7 | * (at your option) any later version. | ||
| 8 | * | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/clk-provider.h> | ||
| 12 | #include <linux/clkdev.h> | ||
| 13 | #include <linux/clk/at91_pmc.h> | ||
| 14 | #include <linux/interrupt.h> | ||
| 15 | #include <linux/irq.h> | ||
| 16 | #include <linux/of.h> | ||
| 17 | #include <linux/of_address.h> | ||
| 18 | #include <linux/of_irq.h> | ||
| 19 | #include <linux/io.h> | ||
| 20 | #include <linux/sched.h> | ||
| 21 | #include <linux/wait.h> | ||
| 22 | |||
| 23 | #include "pmc.h" | ||
| 24 | |||
| 25 | #define UTMI_FIXED_MUL 40 | ||
| 26 | |||
| 27 | struct clk_utmi { | ||
| 28 | struct clk_hw hw; | ||
| 29 | struct at91_pmc *pmc; | ||
| 30 | unsigned int irq; | ||
| 31 | wait_queue_head_t wait; | ||
| 32 | }; | ||
| 33 | |||
| 34 | #define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw) | ||
| 35 | |||
| 36 | static irqreturn_t clk_utmi_irq_handler(int irq, void *dev_id) | ||
| 37 | { | ||
| 38 | struct clk_utmi *utmi = (struct clk_utmi *)dev_id; | ||
| 39 | |||
| 40 | wake_up(&utmi->wait); | ||
| 41 | disable_irq_nosync(utmi->irq); | ||
| 42 | |||
| 43 | return IRQ_HANDLED; | ||
| 44 | } | ||
| 45 | |||
| 46 | static int clk_utmi_prepare(struct clk_hw *hw) | ||
| 47 | { | ||
| 48 | struct clk_utmi *utmi = to_clk_utmi(hw); | ||
| 49 | struct at91_pmc *pmc = utmi->pmc; | ||
| 50 | u32 tmp = at91_pmc_read(AT91_CKGR_UCKR) | AT91_PMC_UPLLEN | | ||
| 51 | AT91_PMC_UPLLCOUNT | AT91_PMC_BIASEN; | ||
| 52 | |||
| 53 | pmc_write(pmc, AT91_CKGR_UCKR, tmp); | ||
| 54 | |||
| 55 | while (!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_LOCKU)) { | ||
| 56 | enable_irq(utmi->irq); | ||
| 57 | wait_event(utmi->wait, | ||
| 58 | pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_LOCKU); | ||
| 59 | } | ||
| 60 | |||
| 61 | return 0; | ||
| 62 | } | ||
| 63 | |||
| 64 | static int clk_utmi_is_prepared(struct clk_hw *hw) | ||
| 65 | { | ||
| 66 | struct clk_utmi *utmi = to_clk_utmi(hw); | ||
| 67 | struct at91_pmc *pmc = utmi->pmc; | ||
| 68 | |||
| 69 | return !!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_LOCKU); | ||
| 70 | } | ||
| 71 | |||
| 72 | static void clk_utmi_unprepare(struct clk_hw *hw) | ||
| 73 | { | ||
| 74 | struct clk_utmi *utmi = to_clk_utmi(hw); | ||
| 75 | struct at91_pmc *pmc = utmi->pmc; | ||
| 76 | u32 tmp = at91_pmc_read(AT91_CKGR_UCKR) & ~AT91_PMC_UPLLEN; | ||
| 77 | |||
| 78 | pmc_write(pmc, AT91_CKGR_UCKR, tmp); | ||
| 79 | } | ||
| 80 | |||
| 81 | static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw, | ||
| 82 | unsigned long parent_rate) | ||
| 83 | { | ||
| 84 | /* UTMI clk is a fixed clk multiplier */ | ||
| 85 | return parent_rate * UTMI_FIXED_MUL; | ||
| 86 | } | ||
| 87 | |||
| 88 | static const struct clk_ops utmi_ops = { | ||
| 89 | .prepare = clk_utmi_prepare, | ||
| 90 | .unprepare = clk_utmi_unprepare, | ||
| 91 | .is_prepared = clk_utmi_is_prepared, | ||
| 92 | .recalc_rate = clk_utmi_recalc_rate, | ||
| 93 | }; | ||
| 94 | |||
| 95 | static struct clk * __init | ||
| 96 | at91_clk_register_utmi(struct at91_pmc *pmc, unsigned int irq, | ||
| 97 | const char *name, const char *parent_name) | ||
| 98 | { | ||
| 99 | int ret; | ||
| 100 | struct clk_utmi *utmi; | ||
| 101 | struct clk *clk = NULL; | ||
| 102 | struct clk_init_data init; | ||
| 103 | |||
| 104 | utmi = kzalloc(sizeof(*utmi), GFP_KERNEL); | ||
| 105 | if (!utmi) | ||
| 106 | return ERR_PTR(-ENOMEM); | ||
| 107 | |||
| 108 | init.name = name; | ||
| 109 | init.ops = &utmi_ops; | ||
| 110 | init.parent_names = parent_name ? &parent_name : NULL; | ||
| 111 | init.num_parents = parent_name ? 1 : 0; | ||
| 112 | init.flags = CLK_SET_RATE_GATE; | ||
| 113 | |||
| 114 | utmi->hw.init = &init; | ||
| 115 | utmi->pmc = pmc; | ||
| 116 | utmi->irq = irq; | ||
| 117 | init_waitqueue_head(&utmi->wait); | ||
| 118 | irq_set_status_flags(utmi->irq, IRQ_NOAUTOEN); | ||
| 119 | ret = request_irq(utmi->irq, clk_utmi_irq_handler, | ||
| 120 | IRQF_TRIGGER_HIGH, "clk-utmi", utmi); | ||
| 121 | if (ret) | ||
| 122 | return ERR_PTR(ret); | ||
| 123 | |||
| 124 | clk = clk_register(NULL, &utmi->hw); | ||
| 125 | if (IS_ERR(clk)) | ||
| 126 | kfree(utmi); | ||
| 127 | |||
| 128 | return clk; | ||
| 129 | } | ||
