1 /* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> 7 */ 8 9 #include <linux/module.h> 10 #include <linux/mutex.h> 11 #include <linux/err.h> 12 #include <linux/clk.h> 13 #include <bcm63xx_cpu.h> 14 #include <bcm63xx_io.h> 15 #include <bcm63xx_regs.h> 16 #include <bcm63xx_clk.h> 17 18 static DEFINE_MUTEX(clocks_mutex); 19 20 21 static void clk_enable_unlocked(struct clk *clk) 22 { 23 if (clk->set && (clk->usage++) == 0) 24 clk->set(clk, 1); 25 } 26 27 static void clk_disable_unlocked(struct clk *clk) 28 { 29 if (clk->set && (--clk->usage) == 0) 30 clk->set(clk, 0); 31 } 32 33 static void bcm_hwclock_set(u32 mask, int enable) 34 { 35 u32 reg; 36 37 reg = bcm_perf_readl(PERF_CKCTL_REG); 38 if (enable) 39 reg |= mask; 40 else 41 reg &= ~mask; 42 bcm_perf_writel(reg, PERF_CKCTL_REG); 43 } 44 45 /* 46 * Ethernet MAC "misc" clock: dma clocks and main clock on 6348 47 */ 48 static void enet_misc_set(struct clk *clk, int enable) 49 { 50 u32 mask; 51 52 if (BCMCPU_IS_6338()) 53 mask = CKCTL_6338_ENET_EN; 54 else if (BCMCPU_IS_6345()) 55 mask = CKCTL_6345_ENET_EN; 56 else if (BCMCPU_IS_6348()) 57 mask = CKCTL_6348_ENET_EN; 58 else 59 /* BCMCPU_IS_6358 */ 60 mask = CKCTL_6358_EMUSB_EN; 61 bcm_hwclock_set(mask, enable); 62 } 63 64 static struct clk clk_enet_misc = { 65 .set = enet_misc_set, 66 }; 67 68 /* 69 * Ethernet MAC clocks: only revelant on 6358, silently enable misc 70 * clocks 71 */ 72 static void enetx_set(struct clk *clk, int enable) 73 { 74 if (enable) 75 clk_enable_unlocked(&clk_enet_misc); 76 else 77 clk_disable_unlocked(&clk_enet_misc); 78 79 if (BCMCPU_IS_6358()) { 80 u32 mask; 81 82 if (clk->id == 0) 83 mask = CKCTL_6358_ENET0_EN; 84 else 85 mask = CKCTL_6358_ENET1_EN; 86 bcm_hwclock_set(mask, enable); 87 } 88 } 89 90 static struct clk clk_enet0 = { 91 .id = 0, 92 .set = enetx_set, 93 }; 94 95 static struct clk clk_enet1 = { 96 .id = 1, 97 .set = enetx_set, 98 }; 99 100 /* 101 * Ethernet PHY clock 102 */ 103 static void ephy_set(struct clk *clk, int enable) 104 { 105 if (!BCMCPU_IS_6358()) 106 return; 107 bcm_hwclock_set(CKCTL_6358_EPHY_EN, enable); 108 } 109 110 111 static struct clk clk_ephy = { 112 .set = ephy_set, 113 }; 114 115 /* 116 * PCM clock 117 */ 118 static void pcm_set(struct clk *clk, int enable) 119 { 120 if (!BCMCPU_IS_6358()) 121 return; 122 bcm_hwclock_set(CKCTL_6358_PCM_EN, enable); 123 } 124 125 static struct clk clk_pcm = { 126 .set = pcm_set, 127 }; 128 129 /* 130 * USB host clock 131 */ 132 static void usbh_set(struct clk *clk, int enable) 133 { 134 if (!BCMCPU_IS_6348()) 135 return; 136 bcm_hwclock_set(CKCTL_6348_USBH_EN, enable); 137 } 138 139 static struct clk clk_usbh = { 140 .set = usbh_set, 141 }; 142 143 /* 144 * SPI clock 145 */ 146 static void spi_set(struct clk *clk, int enable) 147 { 148 u32 mask; 149 150 if (BCMCPU_IS_6338()) 151 mask = CKCTL_6338_SPI_EN; 152 else if (BCMCPU_IS_6348()) 153 mask = CKCTL_6348_SPI_EN; 154 else 155 /* BCMCPU_IS_6358 */ 156 mask = CKCTL_6358_SPI_EN; 157 bcm_hwclock_set(mask, enable); 158 } 159 160 static struct clk clk_spi = { 161 .set = spi_set, 162 }; 163 164 /* 165 * Internal peripheral clock 166 */ 167 static struct clk clk_periph = { 168 .rate = (50 * 1000 * 1000), 169 }; 170 171 172 /* 173 * Linux clock API implementation 174 */ 175 int clk_enable(struct clk *clk) 176 { 177 mutex_lock(&clocks_mutex); 178 clk_enable_unlocked(clk); 179 mutex_unlock(&clocks_mutex); 180 return 0; 181 } 182 183 EXPORT_SYMBOL(clk_enable); 184 185 void clk_disable(struct clk *clk) 186 { 187 mutex_lock(&clocks_mutex); 188 clk_disable_unlocked(clk); 189 mutex_unlock(&clocks_mutex); 190 } 191 192 EXPORT_SYMBOL(clk_disable); 193 194 unsigned long clk_get_rate(struct clk *clk) 195 { 196 return clk->rate; 197 } 198 199 EXPORT_SYMBOL(clk_get_rate); 200 201 struct clk *clk_get(struct device *dev, const char *id) 202 { 203 if (!strcmp(id, "enet0")) 204 return &clk_enet0; 205 if (!strcmp(id, "enet1")) 206 return &clk_enet1; 207 if (!strcmp(id, "ephy")) 208 return &clk_ephy; 209 if (!strcmp(id, "usbh")) 210 return &clk_usbh; 211 if (!strcmp(id, "spi")) 212 return &clk_spi; 213 if (!strcmp(id, "periph")) 214 return &clk_periph; 215 if (BCMCPU_IS_6358() && !strcmp(id, "pcm")) 216 return &clk_pcm; 217 return ERR_PTR(-ENOENT); 218 } 219 220 EXPORT_SYMBOL(clk_get); 221 222 void clk_put(struct clk *clk) 223 { 224 } 225 226 EXPORT_SYMBOL(clk_put); 227