18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Alchemy clocks. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Exposes all configurable internal clock sources to the clk framework. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * We have: 88c2ecf20Sopenharmony_ci * - Root source, usually 12MHz supplied by an external crystal 98c2ecf20Sopenharmony_ci * - 3 PLLs which generate multiples of root rate [AUX, CPU, AUX2] 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Dividers: 128c2ecf20Sopenharmony_ci * - 6 clock dividers with: 138c2ecf20Sopenharmony_ci * * selectable source [one of the PLLs], 148c2ecf20Sopenharmony_ci * * output divided between [2 .. 512 in steps of 2] (!Au1300) 158c2ecf20Sopenharmony_ci * or [1 .. 256 in steps of 1] (Au1300), 168c2ecf20Sopenharmony_ci * * can be enabled individually. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * - up to 6 "internal" (fixed) consumers which: 198c2ecf20Sopenharmony_ci * * take either AUXPLL or one of the above 6 dividers as input, 208c2ecf20Sopenharmony_ci * * divide this input by 1, 2, or 4 (and 3 on Au1300). 218c2ecf20Sopenharmony_ci * * can be disabled separately. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * Misc clocks: 248c2ecf20Sopenharmony_ci * - sysbus clock: CPU core clock (CPUPLL) divided by 2, 3 or 4. 258c2ecf20Sopenharmony_ci * depends on board design and should be set by bootloader, read-only. 268c2ecf20Sopenharmony_ci * - peripheral clock: half the rate of sysbus clock, source for a lot 278c2ecf20Sopenharmony_ci * of peripheral blocks, read-only. 288c2ecf20Sopenharmony_ci * - memory clock: clk rate to main memory chips, depends on board 298c2ecf20Sopenharmony_ci * design and is read-only, 308c2ecf20Sopenharmony_ci * - lrclk: the static bus clock signal for synchronous operation. 318c2ecf20Sopenharmony_ci * depends on board design, must be set by bootloader, 328c2ecf20Sopenharmony_ci * but may be required to correctly configure devices attached to 338c2ecf20Sopenharmony_ci * the static bus. The Au1000/1500/1100 manuals call it LCLK, on 348c2ecf20Sopenharmony_ci * later models it's called RCLK. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include <linux/init.h> 388c2ecf20Sopenharmony_ci#include <linux/io.h> 398c2ecf20Sopenharmony_ci#include <linux/clk.h> 408c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 418c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 428c2ecf20Sopenharmony_ci#include <linux/slab.h> 438c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 448c2ecf20Sopenharmony_ci#include <linux/types.h> 458c2ecf20Sopenharmony_ci#include <asm/mach-au1x00/au1000.h> 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* Base clock: 12MHz is the default in all databooks, and I haven't 488c2ecf20Sopenharmony_ci * found any board yet which uses a different rate. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci#define ALCHEMY_ROOTCLK_RATE 12000000 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* 538c2ecf20Sopenharmony_ci * the internal sources which can be driven by the PLLs and dividers. 548c2ecf20Sopenharmony_ci * Names taken from the databooks, refer to them for more information, 558c2ecf20Sopenharmony_ci * especially which ones are share a clock line. 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_cistatic const char * const alchemy_au1300_intclknames[] = { 588c2ecf20Sopenharmony_ci "lcd_intclk", "gpemgp_clk", "maempe_clk", "maebsa_clk", 598c2ecf20Sopenharmony_ci "EXTCLK0", "EXTCLK1" 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic const char * const alchemy_au1200_intclknames[] = { 638c2ecf20Sopenharmony_ci "lcd_intclk", NULL, NULL, NULL, "EXTCLK0", "EXTCLK1" 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic const char * const alchemy_au1550_intclknames[] = { 678c2ecf20Sopenharmony_ci "usb_clk", "psc0_intclk", "psc1_intclk", "pci_clko", 688c2ecf20Sopenharmony_ci "EXTCLK0", "EXTCLK1" 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic const char * const alchemy_au1100_intclknames[] = { 728c2ecf20Sopenharmony_ci "usb_clk", "lcd_intclk", NULL, "i2s_clk", "EXTCLK0", "EXTCLK1" 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic const char * const alchemy_au1500_intclknames[] = { 768c2ecf20Sopenharmony_ci NULL, "usbd_clk", "usbh_clk", "pci_clko", "EXTCLK0", "EXTCLK1" 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic const char * const alchemy_au1000_intclknames[] = { 808c2ecf20Sopenharmony_ci "irda_clk", "usbd_clk", "usbh_clk", "i2s_clk", "EXTCLK0", 818c2ecf20Sopenharmony_ci "EXTCLK1" 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* aliases for a few on-chip sources which are either shared 858c2ecf20Sopenharmony_ci * or have gone through name changes. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_cistatic struct clk_aliastable { 888c2ecf20Sopenharmony_ci char *alias; 898c2ecf20Sopenharmony_ci char *base; 908c2ecf20Sopenharmony_ci int cputype; 918c2ecf20Sopenharmony_ci} alchemy_clk_aliases[] __initdata = { 928c2ecf20Sopenharmony_ci { "usbh_clk", "usb_clk", ALCHEMY_CPU_AU1100 }, 938c2ecf20Sopenharmony_ci { "usbd_clk", "usb_clk", ALCHEMY_CPU_AU1100 }, 948c2ecf20Sopenharmony_ci { "irda_clk", "usb_clk", ALCHEMY_CPU_AU1100 }, 958c2ecf20Sopenharmony_ci { "usbh_clk", "usb_clk", ALCHEMY_CPU_AU1550 }, 968c2ecf20Sopenharmony_ci { "usbd_clk", "usb_clk", ALCHEMY_CPU_AU1550 }, 978c2ecf20Sopenharmony_ci { "psc2_intclk", "usb_clk", ALCHEMY_CPU_AU1550 }, 988c2ecf20Sopenharmony_ci { "psc3_intclk", "EXTCLK0", ALCHEMY_CPU_AU1550 }, 998c2ecf20Sopenharmony_ci { "psc0_intclk", "EXTCLK0", ALCHEMY_CPU_AU1200 }, 1008c2ecf20Sopenharmony_ci { "psc1_intclk", "EXTCLK1", ALCHEMY_CPU_AU1200 }, 1018c2ecf20Sopenharmony_ci { "psc0_intclk", "EXTCLK0", ALCHEMY_CPU_AU1300 }, 1028c2ecf20Sopenharmony_ci { "psc2_intclk", "EXTCLK0", ALCHEMY_CPU_AU1300 }, 1038c2ecf20Sopenharmony_ci { "psc1_intclk", "EXTCLK1", ALCHEMY_CPU_AU1300 }, 1048c2ecf20Sopenharmony_ci { "psc3_intclk", "EXTCLK1", ALCHEMY_CPU_AU1300 }, 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci { NULL, NULL, 0 }, 1078c2ecf20Sopenharmony_ci}; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci#define IOMEM(x) ((void __iomem *)(KSEG1ADDR(CPHYSADDR(x)))) 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* access locks to SYS_FREQCTRL0/1 and SYS_CLKSRC registers */ 1128c2ecf20Sopenharmony_cistatic spinlock_t alchemy_clk_fg0_lock; 1138c2ecf20Sopenharmony_cistatic spinlock_t alchemy_clk_fg1_lock; 1148c2ecf20Sopenharmony_cistatic spinlock_t alchemy_clk_csrc_lock; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* CPU Core clock *****************************************************/ 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic unsigned long alchemy_clk_cpu_recalc(struct clk_hw *hw, 1198c2ecf20Sopenharmony_ci unsigned long parent_rate) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci unsigned long t; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* 1248c2ecf20Sopenharmony_ci * On early Au1000, sys_cpupll was write-only. Since these 1258c2ecf20Sopenharmony_ci * silicon versions of Au1000 are not sold, we don't bend 1268c2ecf20Sopenharmony_ci * over backwards trying to determine the frequency. 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ci if (unlikely(au1xxx_cpu_has_pll_wo())) 1298c2ecf20Sopenharmony_ci t = 396000000; 1308c2ecf20Sopenharmony_ci else { 1318c2ecf20Sopenharmony_ci t = alchemy_rdsys(AU1000_SYS_CPUPLL) & 0x7f; 1328c2ecf20Sopenharmony_ci if (alchemy_get_cputype() < ALCHEMY_CPU_AU1300) 1338c2ecf20Sopenharmony_ci t &= 0x3f; 1348c2ecf20Sopenharmony_ci t *= parent_rate; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return t; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_civoid __init alchemy_set_lpj(void) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci preset_lpj = alchemy_clk_cpu_recalc(NULL, ALCHEMY_ROOTCLK_RATE); 1438c2ecf20Sopenharmony_ci preset_lpj /= 2 * HZ; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic const struct clk_ops alchemy_clkops_cpu = { 1478c2ecf20Sopenharmony_ci .recalc_rate = alchemy_clk_cpu_recalc, 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic struct clk __init *alchemy_clk_setup_cpu(const char *parent_name, 1518c2ecf20Sopenharmony_ci int ctype) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct clk_init_data id; 1548c2ecf20Sopenharmony_ci struct clk_hw *h; 1558c2ecf20Sopenharmony_ci struct clk *clk; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci h = kzalloc(sizeof(*h), GFP_KERNEL); 1588c2ecf20Sopenharmony_ci if (!h) 1598c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci id.name = ALCHEMY_CPU_CLK; 1628c2ecf20Sopenharmony_ci id.parent_names = &parent_name; 1638c2ecf20Sopenharmony_ci id.num_parents = 1; 1648c2ecf20Sopenharmony_ci id.flags = 0; 1658c2ecf20Sopenharmony_ci id.ops = &alchemy_clkops_cpu; 1668c2ecf20Sopenharmony_ci h->init = &id; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci clk = clk_register(NULL, h); 1698c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 1708c2ecf20Sopenharmony_ci pr_err("failed to register clock\n"); 1718c2ecf20Sopenharmony_ci kfree(h); 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return clk; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci/* AUXPLLs ************************************************************/ 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistruct alchemy_auxpll_clk { 1808c2ecf20Sopenharmony_ci struct clk_hw hw; 1818c2ecf20Sopenharmony_ci unsigned long reg; /* au1300 has also AUXPLL2 */ 1828c2ecf20Sopenharmony_ci int maxmult; /* max multiplier */ 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ci#define to_auxpll_clk(x) container_of(x, struct alchemy_auxpll_clk, hw) 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic unsigned long alchemy_clk_aux_recalc(struct clk_hw *hw, 1878c2ecf20Sopenharmony_ci unsigned long parent_rate) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct alchemy_auxpll_clk *a = to_auxpll_clk(hw); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return (alchemy_rdsys(a->reg) & 0xff) * parent_rate; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic int alchemy_clk_aux_setr(struct clk_hw *hw, 1958c2ecf20Sopenharmony_ci unsigned long rate, 1968c2ecf20Sopenharmony_ci unsigned long parent_rate) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct alchemy_auxpll_clk *a = to_auxpll_clk(hw); 1998c2ecf20Sopenharmony_ci unsigned long d = rate; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (rate) 2028c2ecf20Sopenharmony_ci d /= parent_rate; 2038c2ecf20Sopenharmony_ci else 2048c2ecf20Sopenharmony_ci d = 0; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* minimum is 84MHz, max is 756-1032 depending on variant */ 2078c2ecf20Sopenharmony_ci if (((d < 7) && (d != 0)) || (d > a->maxmult)) 2088c2ecf20Sopenharmony_ci return -EINVAL; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci alchemy_wrsys(d, a->reg); 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic long alchemy_clk_aux_roundr(struct clk_hw *hw, 2158c2ecf20Sopenharmony_ci unsigned long rate, 2168c2ecf20Sopenharmony_ci unsigned long *parent_rate) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct alchemy_auxpll_clk *a = to_auxpll_clk(hw); 2198c2ecf20Sopenharmony_ci unsigned long mult; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (!rate || !*parent_rate) 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci mult = rate / (*parent_rate); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (mult && (mult < 7)) 2278c2ecf20Sopenharmony_ci mult = 7; 2288c2ecf20Sopenharmony_ci if (mult > a->maxmult) 2298c2ecf20Sopenharmony_ci mult = a->maxmult; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return (*parent_rate) * mult; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic const struct clk_ops alchemy_clkops_aux = { 2358c2ecf20Sopenharmony_ci .recalc_rate = alchemy_clk_aux_recalc, 2368c2ecf20Sopenharmony_ci .set_rate = alchemy_clk_aux_setr, 2378c2ecf20Sopenharmony_ci .round_rate = alchemy_clk_aux_roundr, 2388c2ecf20Sopenharmony_ci}; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic struct clk __init *alchemy_clk_setup_aux(const char *parent_name, 2418c2ecf20Sopenharmony_ci char *name, int maxmult, 2428c2ecf20Sopenharmony_ci unsigned long reg) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct clk_init_data id; 2458c2ecf20Sopenharmony_ci struct clk *c; 2468c2ecf20Sopenharmony_ci struct alchemy_auxpll_clk *a; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci a = kzalloc(sizeof(*a), GFP_KERNEL); 2498c2ecf20Sopenharmony_ci if (!a) 2508c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci id.name = name; 2538c2ecf20Sopenharmony_ci id.parent_names = &parent_name; 2548c2ecf20Sopenharmony_ci id.num_parents = 1; 2558c2ecf20Sopenharmony_ci id.flags = CLK_GET_RATE_NOCACHE; 2568c2ecf20Sopenharmony_ci id.ops = &alchemy_clkops_aux; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci a->reg = reg; 2598c2ecf20Sopenharmony_ci a->maxmult = maxmult; 2608c2ecf20Sopenharmony_ci a->hw.init = &id; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci c = clk_register(NULL, &a->hw); 2638c2ecf20Sopenharmony_ci if (!IS_ERR(c)) 2648c2ecf20Sopenharmony_ci clk_register_clkdev(c, name, NULL); 2658c2ecf20Sopenharmony_ci else 2668c2ecf20Sopenharmony_ci kfree(a); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return c; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci/* sysbus_clk *********************************************************/ 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic struct clk __init *alchemy_clk_setup_sysbus(const char *pn) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci unsigned long v = (alchemy_rdsys(AU1000_SYS_POWERCTRL) & 3) + 2; 2768c2ecf20Sopenharmony_ci struct clk *c; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci c = clk_register_fixed_factor(NULL, ALCHEMY_SYSBUS_CLK, 2798c2ecf20Sopenharmony_ci pn, 0, 1, v); 2808c2ecf20Sopenharmony_ci if (!IS_ERR(c)) 2818c2ecf20Sopenharmony_ci clk_register_clkdev(c, ALCHEMY_SYSBUS_CLK, NULL); 2828c2ecf20Sopenharmony_ci return c; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci/* Peripheral Clock ***************************************************/ 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic struct clk __init *alchemy_clk_setup_periph(const char *pn) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci /* Peripheral clock runs at half the rate of sysbus clk */ 2908c2ecf20Sopenharmony_ci struct clk *c; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci c = clk_register_fixed_factor(NULL, ALCHEMY_PERIPH_CLK, 2938c2ecf20Sopenharmony_ci pn, 0, 1, 2); 2948c2ecf20Sopenharmony_ci if (!IS_ERR(c)) 2958c2ecf20Sopenharmony_ci clk_register_clkdev(c, ALCHEMY_PERIPH_CLK, NULL); 2968c2ecf20Sopenharmony_ci return c; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/* mem clock **********************************************************/ 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic struct clk __init *alchemy_clk_setup_mem(const char *pn, int ct) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci void __iomem *addr = IOMEM(AU1000_MEM_PHYS_ADDR); 3048c2ecf20Sopenharmony_ci unsigned long v; 3058c2ecf20Sopenharmony_ci struct clk *c; 3068c2ecf20Sopenharmony_ci int div; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci switch (ct) { 3098c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1550: 3108c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1200: 3118c2ecf20Sopenharmony_ci v = __raw_readl(addr + AU1550_MEM_SDCONFIGB); 3128c2ecf20Sopenharmony_ci div = (v & (1 << 15)) ? 1 : 2; 3138c2ecf20Sopenharmony_ci break; 3148c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1300: 3158c2ecf20Sopenharmony_ci v = __raw_readl(addr + AU1550_MEM_SDCONFIGB); 3168c2ecf20Sopenharmony_ci div = (v & (1 << 31)) ? 1 : 2; 3178c2ecf20Sopenharmony_ci break; 3188c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1000: 3198c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1500: 3208c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1100: 3218c2ecf20Sopenharmony_ci default: 3228c2ecf20Sopenharmony_ci div = 2; 3238c2ecf20Sopenharmony_ci break; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci c = clk_register_fixed_factor(NULL, ALCHEMY_MEM_CLK, pn, 3278c2ecf20Sopenharmony_ci 0, 1, div); 3288c2ecf20Sopenharmony_ci if (!IS_ERR(c)) 3298c2ecf20Sopenharmony_ci clk_register_clkdev(c, ALCHEMY_MEM_CLK, NULL); 3308c2ecf20Sopenharmony_ci return c; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* lrclk: external synchronous static bus clock ***********************/ 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic struct clk __init *alchemy_clk_setup_lrclk(const char *pn, int t) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci /* Au1000, Au1500: MEM_STCFG0[11]: If bit is set, lrclk=pclk/5, 3388c2ecf20Sopenharmony_ci * otherwise lrclk=pclk/4. 3398c2ecf20Sopenharmony_ci * All other variants: MEM_STCFG0[15:13] = divisor. 3408c2ecf20Sopenharmony_ci * L/RCLK = periph_clk / (divisor + 1) 3418c2ecf20Sopenharmony_ci * On Au1000, Au1500, Au1100 it's called LCLK, 3428c2ecf20Sopenharmony_ci * on later models it's called RCLK, but it's the same thing. 3438c2ecf20Sopenharmony_ci */ 3448c2ecf20Sopenharmony_ci struct clk *c; 3458c2ecf20Sopenharmony_ci unsigned long v = alchemy_rdsmem(AU1000_MEM_STCFG0); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci switch (t) { 3488c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1000: 3498c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1500: 3508c2ecf20Sopenharmony_ci v = 4 + ((v >> 11) & 1); 3518c2ecf20Sopenharmony_ci break; 3528c2ecf20Sopenharmony_ci default: /* all other models */ 3538c2ecf20Sopenharmony_ci v = ((v >> 13) & 7) + 1; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci c = clk_register_fixed_factor(NULL, ALCHEMY_LR_CLK, 3568c2ecf20Sopenharmony_ci pn, 0, 1, v); 3578c2ecf20Sopenharmony_ci if (!IS_ERR(c)) 3588c2ecf20Sopenharmony_ci clk_register_clkdev(c, ALCHEMY_LR_CLK, NULL); 3598c2ecf20Sopenharmony_ci return c; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci/* Clock dividers and muxes *******************************************/ 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci/* data for fgen and csrc mux-dividers */ 3658c2ecf20Sopenharmony_cistruct alchemy_fgcs_clk { 3668c2ecf20Sopenharmony_ci struct clk_hw hw; 3678c2ecf20Sopenharmony_ci spinlock_t *reglock; /* register lock */ 3688c2ecf20Sopenharmony_ci unsigned long reg; /* SYS_FREQCTRL0/1 */ 3698c2ecf20Sopenharmony_ci int shift; /* offset in register */ 3708c2ecf20Sopenharmony_ci int parent; /* parent before disable [Au1300] */ 3718c2ecf20Sopenharmony_ci int isen; /* is it enabled? */ 3728c2ecf20Sopenharmony_ci int *dt; /* dividertable for csrc */ 3738c2ecf20Sopenharmony_ci}; 3748c2ecf20Sopenharmony_ci#define to_fgcs_clk(x) container_of(x, struct alchemy_fgcs_clk, hw) 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic long alchemy_calc_div(unsigned long rate, unsigned long prate, 3778c2ecf20Sopenharmony_ci int scale, int maxdiv, unsigned long *rv) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci long div1, div2; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci div1 = prate / rate; 3828c2ecf20Sopenharmony_ci if ((prate / div1) > rate) 3838c2ecf20Sopenharmony_ci div1++; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (scale == 2) { /* only div-by-multiple-of-2 possible */ 3868c2ecf20Sopenharmony_ci if (div1 & 1) 3878c2ecf20Sopenharmony_ci div1++; /* stay <=prate */ 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci div2 = (div1 / scale) - 1; /* value to write to register */ 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (div2 > maxdiv) 3938c2ecf20Sopenharmony_ci div2 = maxdiv; 3948c2ecf20Sopenharmony_ci if (rv) 3958c2ecf20Sopenharmony_ci *rv = div2; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci div1 = ((div2 + 1) * scale); 3988c2ecf20Sopenharmony_ci return div1; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic int alchemy_clk_fgcs_detr(struct clk_hw *hw, 4028c2ecf20Sopenharmony_ci struct clk_rate_request *req, 4038c2ecf20Sopenharmony_ci int scale, int maxdiv) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct clk_hw *pc, *bpc, *free; 4068c2ecf20Sopenharmony_ci long tdv, tpr, pr, nr, br, bpr, diff, lastdiff; 4078c2ecf20Sopenharmony_ci int j; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci lastdiff = INT_MAX; 4108c2ecf20Sopenharmony_ci bpr = 0; 4118c2ecf20Sopenharmony_ci bpc = NULL; 4128c2ecf20Sopenharmony_ci br = -EINVAL; 4138c2ecf20Sopenharmony_ci free = NULL; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* look at the rates each enabled parent supplies and select 4168c2ecf20Sopenharmony_ci * the one that gets closest to but not over the requested rate. 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_ci for (j = 0; j < 7; j++) { 4198c2ecf20Sopenharmony_ci pc = clk_hw_get_parent_by_index(hw, j); 4208c2ecf20Sopenharmony_ci if (!pc) 4218c2ecf20Sopenharmony_ci break; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* if this parent is currently unused, remember it. 4248c2ecf20Sopenharmony_ci * XXX: we would actually want clk_has_active_children() 4258c2ecf20Sopenharmony_ci * but this is a good-enough approximation for now. 4268c2ecf20Sopenharmony_ci */ 4278c2ecf20Sopenharmony_ci if (!clk_hw_is_prepared(pc)) { 4288c2ecf20Sopenharmony_ci if (!free) 4298c2ecf20Sopenharmony_ci free = pc; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci pr = clk_hw_get_rate(pc); 4338c2ecf20Sopenharmony_ci if (pr < req->rate) 4348c2ecf20Sopenharmony_ci continue; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* what can hardware actually provide */ 4378c2ecf20Sopenharmony_ci tdv = alchemy_calc_div(req->rate, pr, scale, maxdiv, NULL); 4388c2ecf20Sopenharmony_ci nr = pr / tdv; 4398c2ecf20Sopenharmony_ci diff = req->rate - nr; 4408c2ecf20Sopenharmony_ci if (nr > req->rate) 4418c2ecf20Sopenharmony_ci continue; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (diff < lastdiff) { 4448c2ecf20Sopenharmony_ci lastdiff = diff; 4458c2ecf20Sopenharmony_ci bpr = pr; 4468c2ecf20Sopenharmony_ci bpc = pc; 4478c2ecf20Sopenharmony_ci br = nr; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci if (diff == 0) 4508c2ecf20Sopenharmony_ci break; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* if we couldn't get the exact rate we wanted from the enabled 4548c2ecf20Sopenharmony_ci * parents, maybe we can tell an available disabled/inactive one 4558c2ecf20Sopenharmony_ci * to give us a rate we can divide down to the requested rate. 4568c2ecf20Sopenharmony_ci */ 4578c2ecf20Sopenharmony_ci if (lastdiff && free) { 4588c2ecf20Sopenharmony_ci for (j = (maxdiv == 4) ? 1 : scale; j <= maxdiv; j += scale) { 4598c2ecf20Sopenharmony_ci tpr = req->rate * j; 4608c2ecf20Sopenharmony_ci if (tpr < 0) 4618c2ecf20Sopenharmony_ci break; 4628c2ecf20Sopenharmony_ci pr = clk_hw_round_rate(free, tpr); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci tdv = alchemy_calc_div(req->rate, pr, scale, maxdiv, 4658c2ecf20Sopenharmony_ci NULL); 4668c2ecf20Sopenharmony_ci nr = pr / tdv; 4678c2ecf20Sopenharmony_ci diff = req->rate - nr; 4688c2ecf20Sopenharmony_ci if (nr > req->rate) 4698c2ecf20Sopenharmony_ci continue; 4708c2ecf20Sopenharmony_ci if (diff < lastdiff) { 4718c2ecf20Sopenharmony_ci lastdiff = diff; 4728c2ecf20Sopenharmony_ci bpr = pr; 4738c2ecf20Sopenharmony_ci bpc = free; 4748c2ecf20Sopenharmony_ci br = nr; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci if (diff == 0) 4778c2ecf20Sopenharmony_ci break; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (br < 0) 4828c2ecf20Sopenharmony_ci return br; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci req->best_parent_rate = bpr; 4858c2ecf20Sopenharmony_ci req->best_parent_hw = bpc; 4868c2ecf20Sopenharmony_ci req->rate = br; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return 0; 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic int alchemy_clk_fgv1_en(struct clk_hw *hw) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 4948c2ecf20Sopenharmony_ci unsigned long v, flags; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci spin_lock_irqsave(c->reglock, flags); 4978c2ecf20Sopenharmony_ci v = alchemy_rdsys(c->reg); 4988c2ecf20Sopenharmony_ci v |= (1 << 1) << c->shift; 4998c2ecf20Sopenharmony_ci alchemy_wrsys(v, c->reg); 5008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(c->reglock, flags); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic int alchemy_clk_fgv1_isen(struct clk_hw *hw) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 5088c2ecf20Sopenharmony_ci unsigned long v = alchemy_rdsys(c->reg) >> (c->shift + 1); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return v & 1; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic void alchemy_clk_fgv1_dis(struct clk_hw *hw) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 5168c2ecf20Sopenharmony_ci unsigned long v, flags; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci spin_lock_irqsave(c->reglock, flags); 5198c2ecf20Sopenharmony_ci v = alchemy_rdsys(c->reg); 5208c2ecf20Sopenharmony_ci v &= ~((1 << 1) << c->shift); 5218c2ecf20Sopenharmony_ci alchemy_wrsys(v, c->reg); 5228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(c->reglock, flags); 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic int alchemy_clk_fgv1_setp(struct clk_hw *hw, u8 index) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 5288c2ecf20Sopenharmony_ci unsigned long v, flags; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci spin_lock_irqsave(c->reglock, flags); 5318c2ecf20Sopenharmony_ci v = alchemy_rdsys(c->reg); 5328c2ecf20Sopenharmony_ci if (index) 5338c2ecf20Sopenharmony_ci v |= (1 << c->shift); 5348c2ecf20Sopenharmony_ci else 5358c2ecf20Sopenharmony_ci v &= ~(1 << c->shift); 5368c2ecf20Sopenharmony_ci alchemy_wrsys(v, c->reg); 5378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(c->reglock, flags); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci return 0; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic u8 alchemy_clk_fgv1_getp(struct clk_hw *hw) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci return (alchemy_rdsys(c->reg) >> c->shift) & 1; 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic int alchemy_clk_fgv1_setr(struct clk_hw *hw, unsigned long rate, 5508c2ecf20Sopenharmony_ci unsigned long parent_rate) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 5538c2ecf20Sopenharmony_ci unsigned long div, v, flags, ret; 5548c2ecf20Sopenharmony_ci int sh = c->shift + 2; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (!rate || !parent_rate || rate > (parent_rate / 2)) 5578c2ecf20Sopenharmony_ci return -EINVAL; 5588c2ecf20Sopenharmony_ci ret = alchemy_calc_div(rate, parent_rate, 2, 512, &div); 5598c2ecf20Sopenharmony_ci spin_lock_irqsave(c->reglock, flags); 5608c2ecf20Sopenharmony_ci v = alchemy_rdsys(c->reg); 5618c2ecf20Sopenharmony_ci v &= ~(0xff << sh); 5628c2ecf20Sopenharmony_ci v |= div << sh; 5638c2ecf20Sopenharmony_ci alchemy_wrsys(v, c->reg); 5648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(c->reglock, flags); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci return 0; 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic unsigned long alchemy_clk_fgv1_recalc(struct clk_hw *hw, 5708c2ecf20Sopenharmony_ci unsigned long parent_rate) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 5738c2ecf20Sopenharmony_ci unsigned long v = alchemy_rdsys(c->reg) >> (c->shift + 2); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci v = ((v & 0xff) + 1) * 2; 5768c2ecf20Sopenharmony_ci return parent_rate / v; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic int alchemy_clk_fgv1_detr(struct clk_hw *hw, 5808c2ecf20Sopenharmony_ci struct clk_rate_request *req) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci return alchemy_clk_fgcs_detr(hw, req, 2, 512); 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci/* Au1000, Au1100, Au15x0, Au12x0 */ 5868c2ecf20Sopenharmony_cistatic const struct clk_ops alchemy_clkops_fgenv1 = { 5878c2ecf20Sopenharmony_ci .recalc_rate = alchemy_clk_fgv1_recalc, 5888c2ecf20Sopenharmony_ci .determine_rate = alchemy_clk_fgv1_detr, 5898c2ecf20Sopenharmony_ci .set_rate = alchemy_clk_fgv1_setr, 5908c2ecf20Sopenharmony_ci .set_parent = alchemy_clk_fgv1_setp, 5918c2ecf20Sopenharmony_ci .get_parent = alchemy_clk_fgv1_getp, 5928c2ecf20Sopenharmony_ci .enable = alchemy_clk_fgv1_en, 5938c2ecf20Sopenharmony_ci .disable = alchemy_clk_fgv1_dis, 5948c2ecf20Sopenharmony_ci .is_enabled = alchemy_clk_fgv1_isen, 5958c2ecf20Sopenharmony_ci}; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic void __alchemy_clk_fgv2_en(struct alchemy_fgcs_clk *c) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci unsigned long v = alchemy_rdsys(c->reg); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci v &= ~(3 << c->shift); 6028c2ecf20Sopenharmony_ci v |= (c->parent & 3) << c->shift; 6038c2ecf20Sopenharmony_ci alchemy_wrsys(v, c->reg); 6048c2ecf20Sopenharmony_ci c->isen = 1; 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic int alchemy_clk_fgv2_en(struct clk_hw *hw) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 6108c2ecf20Sopenharmony_ci unsigned long flags; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci /* enable by setting the previous parent clock */ 6138c2ecf20Sopenharmony_ci spin_lock_irqsave(c->reglock, flags); 6148c2ecf20Sopenharmony_ci __alchemy_clk_fgv2_en(c); 6158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(c->reglock, flags); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci return 0; 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistatic int alchemy_clk_fgv2_isen(struct clk_hw *hw) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci return ((alchemy_rdsys(c->reg) >> c->shift) & 3) != 0; 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_cistatic void alchemy_clk_fgv2_dis(struct clk_hw *hw) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 6308c2ecf20Sopenharmony_ci unsigned long v, flags; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci spin_lock_irqsave(c->reglock, flags); 6338c2ecf20Sopenharmony_ci v = alchemy_rdsys(c->reg); 6348c2ecf20Sopenharmony_ci v &= ~(3 << c->shift); /* set input mux to "disabled" state */ 6358c2ecf20Sopenharmony_ci alchemy_wrsys(v, c->reg); 6368c2ecf20Sopenharmony_ci c->isen = 0; 6378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(c->reglock, flags); 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic int alchemy_clk_fgv2_setp(struct clk_hw *hw, u8 index) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 6438c2ecf20Sopenharmony_ci unsigned long flags; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci spin_lock_irqsave(c->reglock, flags); 6468c2ecf20Sopenharmony_ci c->parent = index + 1; /* value to write to register */ 6478c2ecf20Sopenharmony_ci if (c->isen) 6488c2ecf20Sopenharmony_ci __alchemy_clk_fgv2_en(c); 6498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(c->reglock, flags); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci return 0; 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic u8 alchemy_clk_fgv2_getp(struct clk_hw *hw) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 6578c2ecf20Sopenharmony_ci unsigned long flags, v; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci spin_lock_irqsave(c->reglock, flags); 6608c2ecf20Sopenharmony_ci v = c->parent - 1; 6618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(c->reglock, flags); 6628c2ecf20Sopenharmony_ci return v; 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci/* fg0-2 and fg4-6 share a "scale"-bit. With this bit cleared, the 6668c2ecf20Sopenharmony_ci * dividers behave exactly as on previous models (dividers are multiples 6678c2ecf20Sopenharmony_ci * of 2); with the bit set, dividers are multiples of 1, halving their 6688c2ecf20Sopenharmony_ci * range, but making them also much more flexible. 6698c2ecf20Sopenharmony_ci */ 6708c2ecf20Sopenharmony_cistatic int alchemy_clk_fgv2_setr(struct clk_hw *hw, unsigned long rate, 6718c2ecf20Sopenharmony_ci unsigned long parent_rate) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 6748c2ecf20Sopenharmony_ci int sh = c->shift + 2; 6758c2ecf20Sopenharmony_ci unsigned long div, v, flags, ret; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (!rate || !parent_rate || rate > parent_rate) 6788c2ecf20Sopenharmony_ci return -EINVAL; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci v = alchemy_rdsys(c->reg) & (1 << 30); /* test "scale" bit */ 6818c2ecf20Sopenharmony_ci ret = alchemy_calc_div(rate, parent_rate, v ? 1 : 2, 6828c2ecf20Sopenharmony_ci v ? 256 : 512, &div); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci spin_lock_irqsave(c->reglock, flags); 6858c2ecf20Sopenharmony_ci v = alchemy_rdsys(c->reg); 6868c2ecf20Sopenharmony_ci v &= ~(0xff << sh); 6878c2ecf20Sopenharmony_ci v |= (div & 0xff) << sh; 6888c2ecf20Sopenharmony_ci alchemy_wrsys(v, c->reg); 6898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(c->reglock, flags); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci return 0; 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic unsigned long alchemy_clk_fgv2_recalc(struct clk_hw *hw, 6958c2ecf20Sopenharmony_ci unsigned long parent_rate) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 6988c2ecf20Sopenharmony_ci int sh = c->shift + 2; 6998c2ecf20Sopenharmony_ci unsigned long v, t; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci v = alchemy_rdsys(c->reg); 7028c2ecf20Sopenharmony_ci t = parent_rate / (((v >> sh) & 0xff) + 1); 7038c2ecf20Sopenharmony_ci if ((v & (1 << 30)) == 0) /* test scale bit */ 7048c2ecf20Sopenharmony_ci t /= 2; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci return t; 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_cistatic int alchemy_clk_fgv2_detr(struct clk_hw *hw, 7108c2ecf20Sopenharmony_ci struct clk_rate_request *req) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 7138c2ecf20Sopenharmony_ci int scale, maxdiv; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci if (alchemy_rdsys(c->reg) & (1 << 30)) { 7168c2ecf20Sopenharmony_ci scale = 1; 7178c2ecf20Sopenharmony_ci maxdiv = 256; 7188c2ecf20Sopenharmony_ci } else { 7198c2ecf20Sopenharmony_ci scale = 2; 7208c2ecf20Sopenharmony_ci maxdiv = 512; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci return alchemy_clk_fgcs_detr(hw, req, scale, maxdiv); 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci/* Au1300 larger input mux, no separate disable bit, flexible divider */ 7278c2ecf20Sopenharmony_cistatic const struct clk_ops alchemy_clkops_fgenv2 = { 7288c2ecf20Sopenharmony_ci .recalc_rate = alchemy_clk_fgv2_recalc, 7298c2ecf20Sopenharmony_ci .determine_rate = alchemy_clk_fgv2_detr, 7308c2ecf20Sopenharmony_ci .set_rate = alchemy_clk_fgv2_setr, 7318c2ecf20Sopenharmony_ci .set_parent = alchemy_clk_fgv2_setp, 7328c2ecf20Sopenharmony_ci .get_parent = alchemy_clk_fgv2_getp, 7338c2ecf20Sopenharmony_ci .enable = alchemy_clk_fgv2_en, 7348c2ecf20Sopenharmony_ci .disable = alchemy_clk_fgv2_dis, 7358c2ecf20Sopenharmony_ci .is_enabled = alchemy_clk_fgv2_isen, 7368c2ecf20Sopenharmony_ci}; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_cistatic const char * const alchemy_clk_fgv1_parents[] = { 7398c2ecf20Sopenharmony_ci ALCHEMY_CPU_CLK, ALCHEMY_AUXPLL_CLK 7408c2ecf20Sopenharmony_ci}; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_cistatic const char * const alchemy_clk_fgv2_parents[] = { 7438c2ecf20Sopenharmony_ci ALCHEMY_AUXPLL2_CLK, ALCHEMY_CPU_CLK, ALCHEMY_AUXPLL_CLK 7448c2ecf20Sopenharmony_ci}; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cistatic const char * const alchemy_clk_fgen_names[] = { 7478c2ecf20Sopenharmony_ci ALCHEMY_FG0_CLK, ALCHEMY_FG1_CLK, ALCHEMY_FG2_CLK, 7488c2ecf20Sopenharmony_ci ALCHEMY_FG3_CLK, ALCHEMY_FG4_CLK, ALCHEMY_FG5_CLK }; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic int __init alchemy_clk_init_fgens(int ctype) 7518c2ecf20Sopenharmony_ci{ 7528c2ecf20Sopenharmony_ci struct clk *c; 7538c2ecf20Sopenharmony_ci struct clk_init_data id; 7548c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *a; 7558c2ecf20Sopenharmony_ci unsigned long v; 7568c2ecf20Sopenharmony_ci int i, ret; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci switch (ctype) { 7598c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1000...ALCHEMY_CPU_AU1200: 7608c2ecf20Sopenharmony_ci id.ops = &alchemy_clkops_fgenv1; 7618c2ecf20Sopenharmony_ci id.parent_names = alchemy_clk_fgv1_parents; 7628c2ecf20Sopenharmony_ci id.num_parents = 2; 7638c2ecf20Sopenharmony_ci break; 7648c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1300: 7658c2ecf20Sopenharmony_ci id.ops = &alchemy_clkops_fgenv2; 7668c2ecf20Sopenharmony_ci id.parent_names = alchemy_clk_fgv2_parents; 7678c2ecf20Sopenharmony_ci id.num_parents = 3; 7688c2ecf20Sopenharmony_ci break; 7698c2ecf20Sopenharmony_ci default: 7708c2ecf20Sopenharmony_ci return -ENODEV; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci id.flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci a = kzalloc((sizeof(*a)) * 6, GFP_KERNEL); 7758c2ecf20Sopenharmony_ci if (!a) 7768c2ecf20Sopenharmony_ci return -ENOMEM; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci spin_lock_init(&alchemy_clk_fg0_lock); 7798c2ecf20Sopenharmony_ci spin_lock_init(&alchemy_clk_fg1_lock); 7808c2ecf20Sopenharmony_ci ret = 0; 7818c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) { 7828c2ecf20Sopenharmony_ci id.name = alchemy_clk_fgen_names[i]; 7838c2ecf20Sopenharmony_ci a->shift = 10 * (i < 3 ? i : i - 3); 7848c2ecf20Sopenharmony_ci if (i > 2) { 7858c2ecf20Sopenharmony_ci a->reg = AU1000_SYS_FREQCTRL1; 7868c2ecf20Sopenharmony_ci a->reglock = &alchemy_clk_fg1_lock; 7878c2ecf20Sopenharmony_ci } else { 7888c2ecf20Sopenharmony_ci a->reg = AU1000_SYS_FREQCTRL0; 7898c2ecf20Sopenharmony_ci a->reglock = &alchemy_clk_fg0_lock; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci /* default to first parent if bootloader has set 7938c2ecf20Sopenharmony_ci * the mux to disabled state. 7948c2ecf20Sopenharmony_ci */ 7958c2ecf20Sopenharmony_ci if (ctype == ALCHEMY_CPU_AU1300) { 7968c2ecf20Sopenharmony_ci v = alchemy_rdsys(a->reg); 7978c2ecf20Sopenharmony_ci a->parent = (v >> a->shift) & 3; 7988c2ecf20Sopenharmony_ci if (!a->parent) { 7998c2ecf20Sopenharmony_ci a->parent = 1; 8008c2ecf20Sopenharmony_ci a->isen = 0; 8018c2ecf20Sopenharmony_ci } else 8028c2ecf20Sopenharmony_ci a->isen = 1; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci a->hw.init = &id; 8068c2ecf20Sopenharmony_ci c = clk_register(NULL, &a->hw); 8078c2ecf20Sopenharmony_ci if (IS_ERR(c)) 8088c2ecf20Sopenharmony_ci ret++; 8098c2ecf20Sopenharmony_ci else 8108c2ecf20Sopenharmony_ci clk_register_clkdev(c, id.name, NULL); 8118c2ecf20Sopenharmony_ci a++; 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci return ret; 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci/* internal sources muxes *********************************************/ 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_cistatic int alchemy_clk_csrc_isen(struct clk_hw *hw) 8208c2ecf20Sopenharmony_ci{ 8218c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 8228c2ecf20Sopenharmony_ci unsigned long v = alchemy_rdsys(c->reg); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci return (((v >> c->shift) >> 2) & 7) != 0; 8258c2ecf20Sopenharmony_ci} 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_cistatic void __alchemy_clk_csrc_en(struct alchemy_fgcs_clk *c) 8288c2ecf20Sopenharmony_ci{ 8298c2ecf20Sopenharmony_ci unsigned long v = alchemy_rdsys(c->reg); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci v &= ~((7 << 2) << c->shift); 8328c2ecf20Sopenharmony_ci v |= ((c->parent & 7) << 2) << c->shift; 8338c2ecf20Sopenharmony_ci alchemy_wrsys(v, c->reg); 8348c2ecf20Sopenharmony_ci c->isen = 1; 8358c2ecf20Sopenharmony_ci} 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_cistatic int alchemy_clk_csrc_en(struct clk_hw *hw) 8388c2ecf20Sopenharmony_ci{ 8398c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 8408c2ecf20Sopenharmony_ci unsigned long flags; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci /* enable by setting the previous parent clock */ 8438c2ecf20Sopenharmony_ci spin_lock_irqsave(c->reglock, flags); 8448c2ecf20Sopenharmony_ci __alchemy_clk_csrc_en(c); 8458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(c->reglock, flags); 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci return 0; 8488c2ecf20Sopenharmony_ci} 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_cistatic void alchemy_clk_csrc_dis(struct clk_hw *hw) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 8538c2ecf20Sopenharmony_ci unsigned long v, flags; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci spin_lock_irqsave(c->reglock, flags); 8568c2ecf20Sopenharmony_ci v = alchemy_rdsys(c->reg); 8578c2ecf20Sopenharmony_ci v &= ~((3 << 2) << c->shift); /* mux to "disabled" state */ 8588c2ecf20Sopenharmony_ci alchemy_wrsys(v, c->reg); 8598c2ecf20Sopenharmony_ci c->isen = 0; 8608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(c->reglock, flags); 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_cistatic int alchemy_clk_csrc_setp(struct clk_hw *hw, u8 index) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 8668c2ecf20Sopenharmony_ci unsigned long flags; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci spin_lock_irqsave(c->reglock, flags); 8698c2ecf20Sopenharmony_ci c->parent = index + 1; /* value to write to register */ 8708c2ecf20Sopenharmony_ci if (c->isen) 8718c2ecf20Sopenharmony_ci __alchemy_clk_csrc_en(c); 8728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(c->reglock, flags); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci return 0; 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cistatic u8 alchemy_clk_csrc_getp(struct clk_hw *hw) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci return c->parent - 1; 8828c2ecf20Sopenharmony_ci} 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_cistatic unsigned long alchemy_clk_csrc_recalc(struct clk_hw *hw, 8858c2ecf20Sopenharmony_ci unsigned long parent_rate) 8868c2ecf20Sopenharmony_ci{ 8878c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 8888c2ecf20Sopenharmony_ci unsigned long v = (alchemy_rdsys(c->reg) >> c->shift) & 3; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci return parent_rate / c->dt[v]; 8918c2ecf20Sopenharmony_ci} 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_cistatic int alchemy_clk_csrc_setr(struct clk_hw *hw, unsigned long rate, 8948c2ecf20Sopenharmony_ci unsigned long parent_rate) 8958c2ecf20Sopenharmony_ci{ 8968c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 8978c2ecf20Sopenharmony_ci unsigned long d, v, flags; 8988c2ecf20Sopenharmony_ci int i; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci if (!rate || !parent_rate || rate > parent_rate) 9018c2ecf20Sopenharmony_ci return -EINVAL; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci d = (parent_rate + (rate / 2)) / rate; 9048c2ecf20Sopenharmony_ci if (d > 4) 9058c2ecf20Sopenharmony_ci return -EINVAL; 9068c2ecf20Sopenharmony_ci if ((d == 3) && (c->dt[2] != 3)) 9078c2ecf20Sopenharmony_ci d = 4; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 9108c2ecf20Sopenharmony_ci if (c->dt[i] == d) 9118c2ecf20Sopenharmony_ci break; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci if (i >= 4) 9148c2ecf20Sopenharmony_ci return -EINVAL; /* oops */ 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci spin_lock_irqsave(c->reglock, flags); 9178c2ecf20Sopenharmony_ci v = alchemy_rdsys(c->reg); 9188c2ecf20Sopenharmony_ci v &= ~(3 << c->shift); 9198c2ecf20Sopenharmony_ci v |= (i & 3) << c->shift; 9208c2ecf20Sopenharmony_ci alchemy_wrsys(v, c->reg); 9218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(c->reglock, flags); 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci return 0; 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_cistatic int alchemy_clk_csrc_detr(struct clk_hw *hw, 9278c2ecf20Sopenharmony_ci struct clk_rate_request *req) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); 9308c2ecf20Sopenharmony_ci int scale = c->dt[2] == 3 ? 1 : 2; /* au1300 check */ 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci return alchemy_clk_fgcs_detr(hw, req, scale, 4); 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_cistatic const struct clk_ops alchemy_clkops_csrc = { 9368c2ecf20Sopenharmony_ci .recalc_rate = alchemy_clk_csrc_recalc, 9378c2ecf20Sopenharmony_ci .determine_rate = alchemy_clk_csrc_detr, 9388c2ecf20Sopenharmony_ci .set_rate = alchemy_clk_csrc_setr, 9398c2ecf20Sopenharmony_ci .set_parent = alchemy_clk_csrc_setp, 9408c2ecf20Sopenharmony_ci .get_parent = alchemy_clk_csrc_getp, 9418c2ecf20Sopenharmony_ci .enable = alchemy_clk_csrc_en, 9428c2ecf20Sopenharmony_ci .disable = alchemy_clk_csrc_dis, 9438c2ecf20Sopenharmony_ci .is_enabled = alchemy_clk_csrc_isen, 9448c2ecf20Sopenharmony_ci}; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_cistatic const char * const alchemy_clk_csrc_parents[] = { 9478c2ecf20Sopenharmony_ci /* disabled at index 0 */ ALCHEMY_AUXPLL_CLK, 9488c2ecf20Sopenharmony_ci ALCHEMY_FG0_CLK, ALCHEMY_FG1_CLK, ALCHEMY_FG2_CLK, 9498c2ecf20Sopenharmony_ci ALCHEMY_FG3_CLK, ALCHEMY_FG4_CLK, ALCHEMY_FG5_CLK 9508c2ecf20Sopenharmony_ci}; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci/* divider tables */ 9538c2ecf20Sopenharmony_cistatic int alchemy_csrc_dt1[] = { 1, 4, 1, 2 }; /* rest */ 9548c2ecf20Sopenharmony_cistatic int alchemy_csrc_dt2[] = { 1, 4, 3, 2 }; /* Au1300 */ 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_cistatic int __init alchemy_clk_setup_imux(int ctype) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci struct alchemy_fgcs_clk *a; 9598c2ecf20Sopenharmony_ci const char * const *names; 9608c2ecf20Sopenharmony_ci struct clk_init_data id; 9618c2ecf20Sopenharmony_ci unsigned long v; 9628c2ecf20Sopenharmony_ci int i, ret, *dt; 9638c2ecf20Sopenharmony_ci struct clk *c; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci id.ops = &alchemy_clkops_csrc; 9668c2ecf20Sopenharmony_ci id.parent_names = alchemy_clk_csrc_parents; 9678c2ecf20Sopenharmony_ci id.num_parents = 7; 9688c2ecf20Sopenharmony_ci id.flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci dt = alchemy_csrc_dt1; 9718c2ecf20Sopenharmony_ci switch (ctype) { 9728c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1000: 9738c2ecf20Sopenharmony_ci names = alchemy_au1000_intclknames; 9748c2ecf20Sopenharmony_ci break; 9758c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1500: 9768c2ecf20Sopenharmony_ci names = alchemy_au1500_intclknames; 9778c2ecf20Sopenharmony_ci break; 9788c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1100: 9798c2ecf20Sopenharmony_ci names = alchemy_au1100_intclknames; 9808c2ecf20Sopenharmony_ci break; 9818c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1550: 9828c2ecf20Sopenharmony_ci names = alchemy_au1550_intclknames; 9838c2ecf20Sopenharmony_ci break; 9848c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1200: 9858c2ecf20Sopenharmony_ci names = alchemy_au1200_intclknames; 9868c2ecf20Sopenharmony_ci break; 9878c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1300: 9888c2ecf20Sopenharmony_ci dt = alchemy_csrc_dt2; 9898c2ecf20Sopenharmony_ci names = alchemy_au1300_intclknames; 9908c2ecf20Sopenharmony_ci break; 9918c2ecf20Sopenharmony_ci default: 9928c2ecf20Sopenharmony_ci return -ENODEV; 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci a = kcalloc(6, sizeof(*a), GFP_KERNEL); 9968c2ecf20Sopenharmony_ci if (!a) 9978c2ecf20Sopenharmony_ci return -ENOMEM; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci spin_lock_init(&alchemy_clk_csrc_lock); 10008c2ecf20Sopenharmony_ci ret = 0; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) { 10038c2ecf20Sopenharmony_ci id.name = names[i]; 10048c2ecf20Sopenharmony_ci if (!id.name) 10058c2ecf20Sopenharmony_ci goto next; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci a->shift = i * 5; 10088c2ecf20Sopenharmony_ci a->reg = AU1000_SYS_CLKSRC; 10098c2ecf20Sopenharmony_ci a->reglock = &alchemy_clk_csrc_lock; 10108c2ecf20Sopenharmony_ci a->dt = dt; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci /* default to first parent clock if mux is initially 10138c2ecf20Sopenharmony_ci * set to disabled state. 10148c2ecf20Sopenharmony_ci */ 10158c2ecf20Sopenharmony_ci v = alchemy_rdsys(a->reg); 10168c2ecf20Sopenharmony_ci a->parent = ((v >> a->shift) >> 2) & 7; 10178c2ecf20Sopenharmony_ci if (!a->parent) { 10188c2ecf20Sopenharmony_ci a->parent = 1; 10198c2ecf20Sopenharmony_ci a->isen = 0; 10208c2ecf20Sopenharmony_ci } else 10218c2ecf20Sopenharmony_ci a->isen = 1; 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci a->hw.init = &id; 10248c2ecf20Sopenharmony_ci c = clk_register(NULL, &a->hw); 10258c2ecf20Sopenharmony_ci if (IS_ERR(c)) 10268c2ecf20Sopenharmony_ci ret++; 10278c2ecf20Sopenharmony_ci else 10288c2ecf20Sopenharmony_ci clk_register_clkdev(c, id.name, NULL); 10298c2ecf20Sopenharmony_cinext: 10308c2ecf20Sopenharmony_ci a++; 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci return ret; 10348c2ecf20Sopenharmony_ci} 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci/**********************************************************************/ 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci#define ERRCK(x) \ 10418c2ecf20Sopenharmony_ci if (IS_ERR(x)) { \ 10428c2ecf20Sopenharmony_ci ret = PTR_ERR(x); \ 10438c2ecf20Sopenharmony_ci goto out; \ 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_cistatic int __init alchemy_clk_init(void) 10478c2ecf20Sopenharmony_ci{ 10488c2ecf20Sopenharmony_ci int ctype = alchemy_get_cputype(), ret, i; 10498c2ecf20Sopenharmony_ci struct clk_aliastable *t = alchemy_clk_aliases; 10508c2ecf20Sopenharmony_ci struct clk *c; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci /* Root of the Alchemy clock tree: external 12MHz crystal osc */ 10538c2ecf20Sopenharmony_ci c = clk_register_fixed_rate(NULL, ALCHEMY_ROOT_CLK, NULL, 10548c2ecf20Sopenharmony_ci 0, ALCHEMY_ROOTCLK_RATE); 10558c2ecf20Sopenharmony_ci ERRCK(c) 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci /* CPU core clock */ 10588c2ecf20Sopenharmony_ci c = alchemy_clk_setup_cpu(ALCHEMY_ROOT_CLK, ctype); 10598c2ecf20Sopenharmony_ci ERRCK(c) 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci /* AUXPLLs: max 1GHz on Au1300, 748MHz on older models */ 10628c2ecf20Sopenharmony_ci i = (ctype == ALCHEMY_CPU_AU1300) ? 84 : 63; 10638c2ecf20Sopenharmony_ci c = alchemy_clk_setup_aux(ALCHEMY_ROOT_CLK, ALCHEMY_AUXPLL_CLK, 10648c2ecf20Sopenharmony_ci i, AU1000_SYS_AUXPLL); 10658c2ecf20Sopenharmony_ci ERRCK(c) 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (ctype == ALCHEMY_CPU_AU1300) { 10688c2ecf20Sopenharmony_ci c = alchemy_clk_setup_aux(ALCHEMY_ROOT_CLK, 10698c2ecf20Sopenharmony_ci ALCHEMY_AUXPLL2_CLK, i, 10708c2ecf20Sopenharmony_ci AU1300_SYS_AUXPLL2); 10718c2ecf20Sopenharmony_ci ERRCK(c) 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci /* sysbus clock: cpu core clock divided by 2, 3 or 4 */ 10758c2ecf20Sopenharmony_ci c = alchemy_clk_setup_sysbus(ALCHEMY_CPU_CLK); 10768c2ecf20Sopenharmony_ci ERRCK(c) 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci /* peripheral clock: runs at half rate of sysbus clk */ 10798c2ecf20Sopenharmony_ci c = alchemy_clk_setup_periph(ALCHEMY_SYSBUS_CLK); 10808c2ecf20Sopenharmony_ci ERRCK(c) 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci /* SDR/DDR memory clock */ 10838c2ecf20Sopenharmony_ci c = alchemy_clk_setup_mem(ALCHEMY_SYSBUS_CLK, ctype); 10848c2ecf20Sopenharmony_ci ERRCK(c) 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci /* L/RCLK: external static bus clock for synchronous mode */ 10878c2ecf20Sopenharmony_ci c = alchemy_clk_setup_lrclk(ALCHEMY_PERIPH_CLK, ctype); 10888c2ecf20Sopenharmony_ci ERRCK(c) 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci /* Frequency dividers 0-5 */ 10918c2ecf20Sopenharmony_ci ret = alchemy_clk_init_fgens(ctype); 10928c2ecf20Sopenharmony_ci if (ret) { 10938c2ecf20Sopenharmony_ci ret = -ENODEV; 10948c2ecf20Sopenharmony_ci goto out; 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci /* diving muxes for internal sources */ 10988c2ecf20Sopenharmony_ci ret = alchemy_clk_setup_imux(ctype); 10998c2ecf20Sopenharmony_ci if (ret) { 11008c2ecf20Sopenharmony_ci ret = -ENODEV; 11018c2ecf20Sopenharmony_ci goto out; 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci /* set up aliases drivers might look for */ 11058c2ecf20Sopenharmony_ci while (t->base) { 11068c2ecf20Sopenharmony_ci if (t->cputype == ctype) 11078c2ecf20Sopenharmony_ci clk_add_alias(t->alias, NULL, t->base, NULL); 11088c2ecf20Sopenharmony_ci t++; 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci pr_info("Alchemy clocktree installed\n"); 11128c2ecf20Sopenharmony_ci return 0; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ciout: 11158c2ecf20Sopenharmony_ci return ret; 11168c2ecf20Sopenharmony_ci} 11178c2ecf20Sopenharmony_cipostcore_initcall(alchemy_clk_init); 1118