1e7300d04SMaxime Bizon /* 2e7300d04SMaxime Bizon * This file is subject to the terms and conditions of the GNU General Public 3e7300d04SMaxime Bizon * License. See the file "COPYING" in the main directory of this archive 4e7300d04SMaxime Bizon * for more details. 5e7300d04SMaxime Bizon * 6e7300d04SMaxime Bizon * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> 7e7300d04SMaxime Bizon */ 8e7300d04SMaxime Bizon 9e7300d04SMaxime Bizon #include <linux/module.h> 10e7300d04SMaxime Bizon #include <linux/mutex.h> 11e7300d04SMaxime Bizon #include <linux/err.h> 12e7300d04SMaxime Bizon #include <linux/clk.h> 1304712f3fSMaxime Bizon #include <linux/delay.h> 14e7300d04SMaxime Bizon #include <bcm63xx_cpu.h> 15e7300d04SMaxime Bizon #include <bcm63xx_io.h> 16e7300d04SMaxime Bizon #include <bcm63xx_regs.h> 17ba00e2e5SJonas Gorski #include <bcm63xx_reset.h> 18042df4faSJonas Gorski 19042df4faSJonas Gorski struct clk { 20042df4faSJonas Gorski void (*set)(struct clk *, int); 21042df4faSJonas Gorski unsigned int rate; 22042df4faSJonas Gorski unsigned int usage; 23042df4faSJonas Gorski int id; 24042df4faSJonas Gorski }; 25e7300d04SMaxime Bizon 26e7300d04SMaxime Bizon static DEFINE_MUTEX(clocks_mutex); 27e7300d04SMaxime Bizon 28e7300d04SMaxime Bizon 29e7300d04SMaxime Bizon static void clk_enable_unlocked(struct clk *clk) 30e7300d04SMaxime Bizon { 31e7300d04SMaxime Bizon if (clk->set && (clk->usage++) == 0) 32e7300d04SMaxime Bizon clk->set(clk, 1); 33e7300d04SMaxime Bizon } 34e7300d04SMaxime Bizon 35e7300d04SMaxime Bizon static void clk_disable_unlocked(struct clk *clk) 36e7300d04SMaxime Bizon { 37e7300d04SMaxime Bizon if (clk->set && (--clk->usage) == 0) 38e7300d04SMaxime Bizon clk->set(clk, 0); 39e7300d04SMaxime Bizon } 40e7300d04SMaxime Bizon 41e7300d04SMaxime Bizon static void bcm_hwclock_set(u32 mask, int enable) 42e7300d04SMaxime Bizon { 43e7300d04SMaxime Bizon u32 reg; 44e7300d04SMaxime Bizon 45e7300d04SMaxime Bizon reg = bcm_perf_readl(PERF_CKCTL_REG); 46e7300d04SMaxime Bizon if (enable) 47e7300d04SMaxime Bizon reg |= mask; 48e7300d04SMaxime Bizon else 49e7300d04SMaxime Bizon reg &= ~mask; 50e7300d04SMaxime Bizon bcm_perf_writel(reg, PERF_CKCTL_REG); 51e7300d04SMaxime Bizon } 52e7300d04SMaxime Bizon 53e7300d04SMaxime Bizon /* 54e7300d04SMaxime Bizon * Ethernet MAC "misc" clock: dma clocks and main clock on 6348 55e7300d04SMaxime Bizon */ 56e7300d04SMaxime Bizon static void enet_misc_set(struct clk *clk, int enable) 57e7300d04SMaxime Bizon { 58e7300d04SMaxime Bizon u32 mask; 59e7300d04SMaxime Bizon 60e7300d04SMaxime Bizon if (BCMCPU_IS_6338()) 61e7300d04SMaxime Bizon mask = CKCTL_6338_ENET_EN; 62e7300d04SMaxime Bizon else if (BCMCPU_IS_6345()) 63e7300d04SMaxime Bizon mask = CKCTL_6345_ENET_EN; 64e7300d04SMaxime Bizon else if (BCMCPU_IS_6348()) 65e7300d04SMaxime Bizon mask = CKCTL_6348_ENET_EN; 66e7300d04SMaxime Bizon else 67e7300d04SMaxime Bizon /* BCMCPU_IS_6358 */ 68e7300d04SMaxime Bizon mask = CKCTL_6358_EMUSB_EN; 69e7300d04SMaxime Bizon bcm_hwclock_set(mask, enable); 70e7300d04SMaxime Bizon } 71e7300d04SMaxime Bizon 72e7300d04SMaxime Bizon static struct clk clk_enet_misc = { 73e7300d04SMaxime Bizon .set = enet_misc_set, 74e7300d04SMaxime Bizon }; 75e7300d04SMaxime Bizon 76e7300d04SMaxime Bizon /* 77e7300d04SMaxime Bizon * Ethernet MAC clocks: only revelant on 6358, silently enable misc 78e7300d04SMaxime Bizon * clocks 79e7300d04SMaxime Bizon */ 80e7300d04SMaxime Bizon static void enetx_set(struct clk *clk, int enable) 81e7300d04SMaxime Bizon { 82e7300d04SMaxime Bizon if (enable) 83e7300d04SMaxime Bizon clk_enable_unlocked(&clk_enet_misc); 84e7300d04SMaxime Bizon else 85e7300d04SMaxime Bizon clk_disable_unlocked(&clk_enet_misc); 86e7300d04SMaxime Bizon 877b933421SFlorian Fainelli if (BCMCPU_IS_3368() || BCMCPU_IS_6358()) { 88e7300d04SMaxime Bizon u32 mask; 89e7300d04SMaxime Bizon 90e7300d04SMaxime Bizon if (clk->id == 0) 91e7300d04SMaxime Bizon mask = CKCTL_6358_ENET0_EN; 92e7300d04SMaxime Bizon else 93e7300d04SMaxime Bizon mask = CKCTL_6358_ENET1_EN; 94e7300d04SMaxime Bizon bcm_hwclock_set(mask, enable); 95e7300d04SMaxime Bizon } 96e7300d04SMaxime Bizon } 97e7300d04SMaxime Bizon 98e7300d04SMaxime Bizon static struct clk clk_enet0 = { 99e7300d04SMaxime Bizon .id = 0, 100e7300d04SMaxime Bizon .set = enetx_set, 101e7300d04SMaxime Bizon }; 102e7300d04SMaxime Bizon 103e7300d04SMaxime Bizon static struct clk clk_enet1 = { 104e7300d04SMaxime Bizon .id = 1, 105e7300d04SMaxime Bizon .set = enetx_set, 106e7300d04SMaxime Bizon }; 107e7300d04SMaxime Bizon 108e7300d04SMaxime Bizon /* 109e7300d04SMaxime Bizon * Ethernet PHY clock 110e7300d04SMaxime Bizon */ 111e7300d04SMaxime Bizon static void ephy_set(struct clk *clk, int enable) 112e7300d04SMaxime Bizon { 1137b933421SFlorian Fainelli if (BCMCPU_IS_3368() || BCMCPU_IS_6358()) 114e7300d04SMaxime Bizon bcm_hwclock_set(CKCTL_6358_EPHY_EN, enable); 115e7300d04SMaxime Bizon } 116e7300d04SMaxime Bizon 117e7300d04SMaxime Bizon 118e7300d04SMaxime Bizon static struct clk clk_ephy = { 119e7300d04SMaxime Bizon .set = ephy_set, 120e7300d04SMaxime Bizon }; 121e7300d04SMaxime Bizon 122e7300d04SMaxime Bizon /* 12304712f3fSMaxime Bizon * Ethernet switch clock 12404712f3fSMaxime Bizon */ 12504712f3fSMaxime Bizon static void enetsw_set(struct clk *clk, int enable) 12604712f3fSMaxime Bizon { 1271cd1c049SJonas Gorski if (BCMCPU_IS_6328()) 1281cd1c049SJonas Gorski bcm_hwclock_set(CKCTL_6328_ROBOSW_EN, enable); 1291cd1c049SJonas Gorski else if (BCMCPU_IS_6362()) 1301cd1c049SJonas Gorski bcm_hwclock_set(CKCTL_6362_ROBOSW_EN, enable); 1311cd1c049SJonas Gorski else if (BCMCPU_IS_6368()) 132d9831a41SFlorian Fainelli bcm_hwclock_set(CKCTL_6368_ROBOSW_EN | 13304712f3fSMaxime Bizon CKCTL_6368_SWPKT_USB_EN | 1341cd1c049SJonas Gorski CKCTL_6368_SWPKT_SAR_EN, 1351cd1c049SJonas Gorski enable); 1361cd1c049SJonas Gorski else 1371cd1c049SJonas Gorski return; 1381cd1c049SJonas Gorski 13904712f3fSMaxime Bizon if (enable) { 14004712f3fSMaxime Bizon /* reset switch core afer clock change */ 141ba00e2e5SJonas Gorski bcm63xx_core_set_reset(BCM63XX_RESET_ENETSW, 1); 14204712f3fSMaxime Bizon msleep(10); 143ba00e2e5SJonas Gorski bcm63xx_core_set_reset(BCM63XX_RESET_ENETSW, 0); 14404712f3fSMaxime Bizon msleep(10); 14504712f3fSMaxime Bizon } 14604712f3fSMaxime Bizon } 14704712f3fSMaxime Bizon 14804712f3fSMaxime Bizon static struct clk clk_enetsw = { 14904712f3fSMaxime Bizon .set = enetsw_set, 15004712f3fSMaxime Bizon }; 15104712f3fSMaxime Bizon 15204712f3fSMaxime Bizon /* 153e7300d04SMaxime Bizon * PCM clock 154e7300d04SMaxime Bizon */ 155e7300d04SMaxime Bizon static void pcm_set(struct clk *clk, int enable) 156e7300d04SMaxime Bizon { 1577b933421SFlorian Fainelli if (BCMCPU_IS_3368()) 1587b933421SFlorian Fainelli bcm_hwclock_set(CKCTL_3368_PCM_EN, enable); 1597b933421SFlorian Fainelli if (BCMCPU_IS_6358()) 160e7300d04SMaxime Bizon bcm_hwclock_set(CKCTL_6358_PCM_EN, enable); 161e7300d04SMaxime Bizon } 162e7300d04SMaxime Bizon 163e7300d04SMaxime Bizon static struct clk clk_pcm = { 164e7300d04SMaxime Bizon .set = pcm_set, 165e7300d04SMaxime Bizon }; 166e7300d04SMaxime Bizon 167e7300d04SMaxime Bizon /* 168e7300d04SMaxime Bizon * USB host clock 169e7300d04SMaxime Bizon */ 170e7300d04SMaxime Bizon static void usbh_set(struct clk *clk, int enable) 171e7300d04SMaxime Bizon { 172dd89d60cSKevin Cernekee if (BCMCPU_IS_6328()) 173dd89d60cSKevin Cernekee bcm_hwclock_set(CKCTL_6328_USBH_EN, enable); 174dd89d60cSKevin Cernekee else if (BCMCPU_IS_6348()) 175e7300d04SMaxime Bizon bcm_hwclock_set(CKCTL_6348_USBH_EN, enable); 1761cd1c049SJonas Gorski else if (BCMCPU_IS_6362()) 1771cd1c049SJonas Gorski bcm_hwclock_set(CKCTL_6362_USBH_EN, enable); 17804712f3fSMaxime Bizon else if (BCMCPU_IS_6368()) 179d9831a41SFlorian Fainelli bcm_hwclock_set(CKCTL_6368_USBH_EN, enable); 180e7300d04SMaxime Bizon } 181e7300d04SMaxime Bizon 182e7300d04SMaxime Bizon static struct clk clk_usbh = { 183e7300d04SMaxime Bizon .set = usbh_set, 184e7300d04SMaxime Bizon }; 185e7300d04SMaxime Bizon 186e7300d04SMaxime Bizon /* 187dd89d60cSKevin Cernekee * USB device clock 188dd89d60cSKevin Cernekee */ 189dd89d60cSKevin Cernekee static void usbd_set(struct clk *clk, int enable) 190dd89d60cSKevin Cernekee { 191dd89d60cSKevin Cernekee if (BCMCPU_IS_6328()) 192dd89d60cSKevin Cernekee bcm_hwclock_set(CKCTL_6328_USBD_EN, enable); 1931cd1c049SJonas Gorski else if (BCMCPU_IS_6362()) 1941cd1c049SJonas Gorski bcm_hwclock_set(CKCTL_6362_USBD_EN, enable); 195dd89d60cSKevin Cernekee else if (BCMCPU_IS_6368()) 196dd89d60cSKevin Cernekee bcm_hwclock_set(CKCTL_6368_USBD_EN, enable); 197dd89d60cSKevin Cernekee } 198dd89d60cSKevin Cernekee 199dd89d60cSKevin Cernekee static struct clk clk_usbd = { 200dd89d60cSKevin Cernekee .set = usbd_set, 201dd89d60cSKevin Cernekee }; 202dd89d60cSKevin Cernekee 203dd89d60cSKevin Cernekee /* 204e7300d04SMaxime Bizon * SPI clock 205e7300d04SMaxime Bizon */ 206e7300d04SMaxime Bizon static void spi_set(struct clk *clk, int enable) 207e7300d04SMaxime Bizon { 208e7300d04SMaxime Bizon u32 mask; 209e7300d04SMaxime Bizon 210e7300d04SMaxime Bizon if (BCMCPU_IS_6338()) 211e7300d04SMaxime Bizon mask = CKCTL_6338_SPI_EN; 212e7300d04SMaxime Bizon else if (BCMCPU_IS_6348()) 213e7300d04SMaxime Bizon mask = CKCTL_6348_SPI_EN; 2147b933421SFlorian Fainelli else if (BCMCPU_IS_3368() || BCMCPU_IS_6358()) 215e7300d04SMaxime Bizon mask = CKCTL_6358_SPI_EN; 21608a41d12SJonas Gorski else if (BCMCPU_IS_6362()) 21708a41d12SJonas Gorski mask = CKCTL_6362_SPI_EN; 21819372b24SFlorian Fainelli else 21919372b24SFlorian Fainelli /* BCMCPU_IS_6368 */ 22019372b24SFlorian Fainelli mask = CKCTL_6368_SPI_EN; 221e7300d04SMaxime Bizon bcm_hwclock_set(mask, enable); 222e7300d04SMaxime Bizon } 223e7300d04SMaxime Bizon 224e7300d04SMaxime Bizon static struct clk clk_spi = { 225e7300d04SMaxime Bizon .set = spi_set, 226e7300d04SMaxime Bizon }; 227e7300d04SMaxime Bizon 228e7300d04SMaxime Bizon /* 22904712f3fSMaxime Bizon * XTM clock 23004712f3fSMaxime Bizon */ 23104712f3fSMaxime Bizon static void xtm_set(struct clk *clk, int enable) 23204712f3fSMaxime Bizon { 23304712f3fSMaxime Bizon if (!BCMCPU_IS_6368()) 23404712f3fSMaxime Bizon return; 23504712f3fSMaxime Bizon 236d9831a41SFlorian Fainelli bcm_hwclock_set(CKCTL_6368_SAR_EN | 23704712f3fSMaxime Bizon CKCTL_6368_SWPKT_SAR_EN, enable); 23804712f3fSMaxime Bizon 23904712f3fSMaxime Bizon if (enable) { 24004712f3fSMaxime Bizon /* reset sar core afer clock change */ 241ba00e2e5SJonas Gorski bcm63xx_core_set_reset(BCM63XX_RESET_SAR, 1); 24204712f3fSMaxime Bizon mdelay(1); 243ba00e2e5SJonas Gorski bcm63xx_core_set_reset(BCM63XX_RESET_SAR, 0); 24404712f3fSMaxime Bizon mdelay(1); 24504712f3fSMaxime Bizon } 24604712f3fSMaxime Bizon } 24704712f3fSMaxime Bizon 24804712f3fSMaxime Bizon 24904712f3fSMaxime Bizon static struct clk clk_xtm = { 25004712f3fSMaxime Bizon .set = xtm_set, 25104712f3fSMaxime Bizon }; 25204712f3fSMaxime Bizon 25304712f3fSMaxime Bizon /* 2540b55561bSFlorian Fainelli * IPsec clock 2550b55561bSFlorian Fainelli */ 2560b55561bSFlorian Fainelli static void ipsec_set(struct clk *clk, int enable) 2570b55561bSFlorian Fainelli { 2581cd1c049SJonas Gorski if (BCMCPU_IS_6362()) 2591cd1c049SJonas Gorski bcm_hwclock_set(CKCTL_6362_IPSEC_EN, enable); 2601cd1c049SJonas Gorski else if (BCMCPU_IS_6368()) 2610b55561bSFlorian Fainelli bcm_hwclock_set(CKCTL_6368_IPSEC_EN, enable); 2620b55561bSFlorian Fainelli } 2630b55561bSFlorian Fainelli 2640b55561bSFlorian Fainelli static struct clk clk_ipsec = { 2650b55561bSFlorian Fainelli .set = ipsec_set, 2660b55561bSFlorian Fainelli }; 2670b55561bSFlorian Fainelli 2680b55561bSFlorian Fainelli /* 269f2d1035eSJonas Gorski * PCIe clock 270f2d1035eSJonas Gorski */ 271f2d1035eSJonas Gorski 272f2d1035eSJonas Gorski static void pcie_set(struct clk *clk, int enable) 273f2d1035eSJonas Gorski { 2741cd1c049SJonas Gorski if (BCMCPU_IS_6328()) 275f2d1035eSJonas Gorski bcm_hwclock_set(CKCTL_6328_PCIE_EN, enable); 2761cd1c049SJonas Gorski else if (BCMCPU_IS_6362()) 2771cd1c049SJonas Gorski bcm_hwclock_set(CKCTL_6362_PCIE_EN, enable); 278f2d1035eSJonas Gorski } 279f2d1035eSJonas Gorski 280f2d1035eSJonas Gorski static struct clk clk_pcie = { 281f2d1035eSJonas Gorski .set = pcie_set, 282f2d1035eSJonas Gorski }; 283f2d1035eSJonas Gorski 284f2d1035eSJonas Gorski /* 285e7300d04SMaxime Bizon * Internal peripheral clock 286e7300d04SMaxime Bizon */ 287e7300d04SMaxime Bizon static struct clk clk_periph = { 288e7300d04SMaxime Bizon .rate = (50 * 1000 * 1000), 289e7300d04SMaxime Bizon }; 290e7300d04SMaxime Bizon 291e7300d04SMaxime Bizon 292e7300d04SMaxime Bizon /* 293e7300d04SMaxime Bizon * Linux clock API implementation 294e7300d04SMaxime Bizon */ 295e7300d04SMaxime Bizon int clk_enable(struct clk *clk) 296e7300d04SMaxime Bizon { 297e7300d04SMaxime Bizon mutex_lock(&clocks_mutex); 298e7300d04SMaxime Bizon clk_enable_unlocked(clk); 299e7300d04SMaxime Bizon mutex_unlock(&clocks_mutex); 300e7300d04SMaxime Bizon return 0; 301e7300d04SMaxime Bizon } 302e7300d04SMaxime Bizon 303e7300d04SMaxime Bizon EXPORT_SYMBOL(clk_enable); 304e7300d04SMaxime Bizon 305e7300d04SMaxime Bizon void clk_disable(struct clk *clk) 306e7300d04SMaxime Bizon { 307e7300d04SMaxime Bizon mutex_lock(&clocks_mutex); 308e7300d04SMaxime Bizon clk_disable_unlocked(clk); 309e7300d04SMaxime Bizon mutex_unlock(&clocks_mutex); 310e7300d04SMaxime Bizon } 311e7300d04SMaxime Bizon 312e7300d04SMaxime Bizon EXPORT_SYMBOL(clk_disable); 313e7300d04SMaxime Bizon 314e7300d04SMaxime Bizon unsigned long clk_get_rate(struct clk *clk) 315e7300d04SMaxime Bizon { 316e7300d04SMaxime Bizon return clk->rate; 317e7300d04SMaxime Bizon } 318e7300d04SMaxime Bizon 319e7300d04SMaxime Bizon EXPORT_SYMBOL(clk_get_rate); 320e7300d04SMaxime Bizon 321*7aa2d052SMarkos Chandras int clk_set_rate(struct clk *clk, unsigned long rate) 322*7aa2d052SMarkos Chandras { 323*7aa2d052SMarkos Chandras return 0; 324*7aa2d052SMarkos Chandras } 325*7aa2d052SMarkos Chandras EXPORT_SYMBOL_GPL(clk_set_rate); 326*7aa2d052SMarkos Chandras 327*7aa2d052SMarkos Chandras long clk_round_rate(struct clk *clk, unsigned long rate) 328*7aa2d052SMarkos Chandras { 329*7aa2d052SMarkos Chandras return 0; 330*7aa2d052SMarkos Chandras } 331*7aa2d052SMarkos Chandras EXPORT_SYMBOL_GPL(clk_round_rate); 332*7aa2d052SMarkos Chandras 333e7300d04SMaxime Bizon struct clk *clk_get(struct device *dev, const char *id) 334e7300d04SMaxime Bizon { 335e7300d04SMaxime Bizon if (!strcmp(id, "enet0")) 336e7300d04SMaxime Bizon return &clk_enet0; 337e7300d04SMaxime Bizon if (!strcmp(id, "enet1")) 338e7300d04SMaxime Bizon return &clk_enet1; 33904712f3fSMaxime Bizon if (!strcmp(id, "enetsw")) 34004712f3fSMaxime Bizon return &clk_enetsw; 341e7300d04SMaxime Bizon if (!strcmp(id, "ephy")) 342e7300d04SMaxime Bizon return &clk_ephy; 343e7300d04SMaxime Bizon if (!strcmp(id, "usbh")) 344e7300d04SMaxime Bizon return &clk_usbh; 345dd89d60cSKevin Cernekee if (!strcmp(id, "usbd")) 346dd89d60cSKevin Cernekee return &clk_usbd; 347e7300d04SMaxime Bizon if (!strcmp(id, "spi")) 348e7300d04SMaxime Bizon return &clk_spi; 34904712f3fSMaxime Bizon if (!strcmp(id, "xtm")) 35004712f3fSMaxime Bizon return &clk_xtm; 351e7300d04SMaxime Bizon if (!strcmp(id, "periph")) 352e7300d04SMaxime Bizon return &clk_periph; 3537b933421SFlorian Fainelli if ((BCMCPU_IS_3368() || BCMCPU_IS_6358()) && !strcmp(id, "pcm")) 354e7300d04SMaxime Bizon return &clk_pcm; 3551cd1c049SJonas Gorski if ((BCMCPU_IS_6362() || BCMCPU_IS_6368()) && !strcmp(id, "ipsec")) 3560b55561bSFlorian Fainelli return &clk_ipsec; 3571cd1c049SJonas Gorski if ((BCMCPU_IS_6328() || BCMCPU_IS_6362()) && !strcmp(id, "pcie")) 358f2d1035eSJonas Gorski return &clk_pcie; 359e7300d04SMaxime Bizon return ERR_PTR(-ENOENT); 360e7300d04SMaxime Bizon } 361e7300d04SMaxime Bizon 362e7300d04SMaxime Bizon EXPORT_SYMBOL(clk_get); 363e7300d04SMaxime Bizon 364e7300d04SMaxime Bizon void clk_put(struct clk *clk) 365e7300d04SMaxime Bizon { 366e7300d04SMaxime Bizon } 367e7300d04SMaxime Bizon 368e7300d04SMaxime Bizon EXPORT_SYMBOL(clk_put); 369