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 } 130 131 static void __init 132 of_at91_clk_utmi_setup(struct device_node *np, struct at91_pmc *pmc) 133 { 134 unsigned int irq; 135 struct clk *clk; 136 const char *parent_name; 137 const char *name = np->name; 138 139 parent_name = of_clk_get_parent_name(np, 0); 140 141 of_property_read_string(np, "clock-output-names", &name); 142 143 irq = irq_of_parse_and_map(np, 0); 144 if (!irq) 145 return; 146 147 clk = at91_clk_register_utmi(pmc, irq, name, parent_name); 148 if (IS_ERR(clk)) 149 return; 150 151 of_clk_add_provider(np, of_clk_src_simple_get, clk); 152 return; 153 } 154 155 void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np, 156 struct at91_pmc *pmc) 157 { 158 of_at91_clk_utmi_setup(np, pmc); 159 } 160