162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/clk.h> 462306a36Sopenharmony_ci#include <linux/clkdev.h> 562306a36Sopenharmony_ci#include <linux/clk-provider.h> 662306a36Sopenharmony_ci#include <linux/delay.h> 762306a36Sopenharmony_ci#include <linux/err.h> 862306a36Sopenharmony_ci#include <linux/io.h> 962306a36Sopenharmony_ci#include <linux/math64.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/of_device.h> 1262306a36Sopenharmony_ci#include <linux/string.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define ADPLL_PLLSS_MMR_LOCK_OFFSET 0x00 /* Managed by MPPULL */ 1562306a36Sopenharmony_ci#define ADPLL_PLLSS_MMR_LOCK_ENABLED 0x1f125B64 1662306a36Sopenharmony_ci#define ADPLL_PLLSS_MMR_UNLOCK_MAGIC 0x1eda4c3d 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define ADPLL_PWRCTRL_OFFSET 0x00 1962306a36Sopenharmony_ci#define ADPLL_PWRCTRL_PONIN 5 2062306a36Sopenharmony_ci#define ADPLL_PWRCTRL_PGOODIN 4 2162306a36Sopenharmony_ci#define ADPLL_PWRCTRL_RET 3 2262306a36Sopenharmony_ci#define ADPLL_PWRCTRL_ISORET 2 2362306a36Sopenharmony_ci#define ADPLL_PWRCTRL_ISOSCAN 1 2462306a36Sopenharmony_ci#define ADPLL_PWRCTRL_OFFMODE 0 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define ADPLL_CLKCTRL_OFFSET 0x04 2762306a36Sopenharmony_ci#define ADPLL_CLKCTRL_CLKDCOLDOEN 29 2862306a36Sopenharmony_ci#define ADPLL_CLKCTRL_IDLE 23 2962306a36Sopenharmony_ci#define ADPLL_CLKCTRL_CLKOUTEN 20 3062306a36Sopenharmony_ci#define ADPLL_CLKINPHIFSEL_ADPLL_S 19 /* REVISIT: which bit? */ 3162306a36Sopenharmony_ci#define ADPLL_CLKCTRL_CLKOUTLDOEN_ADPLL_LJ 19 3262306a36Sopenharmony_ci#define ADPLL_CLKCTRL_ULOWCLKEN 18 3362306a36Sopenharmony_ci#define ADPLL_CLKCTRL_CLKDCOLDOPWDNZ 17 3462306a36Sopenharmony_ci#define ADPLL_CLKCTRL_M2PWDNZ 16 3562306a36Sopenharmony_ci#define ADPLL_CLKCTRL_M3PWDNZ_ADPLL_S 15 3662306a36Sopenharmony_ci#define ADPLL_CLKCTRL_LOWCURRSTDBY_ADPLL_S 13 3762306a36Sopenharmony_ci#define ADPLL_CLKCTRL_LPMODE_ADPLL_S 12 3862306a36Sopenharmony_ci#define ADPLL_CLKCTRL_REGM4XEN_ADPLL_S 10 3962306a36Sopenharmony_ci#define ADPLL_CLKCTRL_SELFREQDCO_ADPLL_LJ 10 4062306a36Sopenharmony_ci#define ADPLL_CLKCTRL_TINITZ 0 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define ADPLL_TENABLE_OFFSET 0x08 4362306a36Sopenharmony_ci#define ADPLL_TENABLEDIV_OFFSET 0x8c 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define ADPLL_M2NDIV_OFFSET 0x10 4662306a36Sopenharmony_ci#define ADPLL_M2NDIV_M2 16 4762306a36Sopenharmony_ci#define ADPLL_M2NDIV_M2_ADPLL_S_WIDTH 5 4862306a36Sopenharmony_ci#define ADPLL_M2NDIV_M2_ADPLL_LJ_WIDTH 7 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define ADPLL_MN2DIV_OFFSET 0x14 5162306a36Sopenharmony_ci#define ADPLL_MN2DIV_N2 16 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define ADPLL_FRACDIV_OFFSET 0x18 5462306a36Sopenharmony_ci#define ADPLL_FRACDIV_REGSD 24 5562306a36Sopenharmony_ci#define ADPLL_FRACDIV_FRACTIONALM 0 5662306a36Sopenharmony_ci#define ADPLL_FRACDIV_FRACTIONALM_MASK 0x3ffff 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define ADPLL_BWCTRL_OFFSET 0x1c 5962306a36Sopenharmony_ci#define ADPLL_BWCTRL_BWCONTROL 1 6062306a36Sopenharmony_ci#define ADPLL_BWCTRL_BW_INCR_DECRZ 0 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define ADPLL_RESERVED_OFFSET 0x20 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define ADPLL_STATUS_OFFSET 0x24 6562306a36Sopenharmony_ci#define ADPLL_STATUS_PONOUT 31 6662306a36Sopenharmony_ci#define ADPLL_STATUS_PGOODOUT 30 6762306a36Sopenharmony_ci#define ADPLL_STATUS_LDOPWDN 29 6862306a36Sopenharmony_ci#define ADPLL_STATUS_RECAL_BSTATUS3 28 6962306a36Sopenharmony_ci#define ADPLL_STATUS_RECAL_OPPIN 27 7062306a36Sopenharmony_ci#define ADPLL_STATUS_PHASELOCK 10 7162306a36Sopenharmony_ci#define ADPLL_STATUS_FREQLOCK 9 7262306a36Sopenharmony_ci#define ADPLL_STATUS_BYPASSACK 8 7362306a36Sopenharmony_ci#define ADPLL_STATUS_LOSSREF 6 7462306a36Sopenharmony_ci#define ADPLL_STATUS_CLKOUTENACK 5 7562306a36Sopenharmony_ci#define ADPLL_STATUS_LOCK2 4 7662306a36Sopenharmony_ci#define ADPLL_STATUS_M2CHANGEACK 3 7762306a36Sopenharmony_ci#define ADPLL_STATUS_HIGHJITTER 1 7862306a36Sopenharmony_ci#define ADPLL_STATUS_BYPASS 0 7962306a36Sopenharmony_ci#define ADPLL_STATUS_PREPARED_MASK (BIT(ADPLL_STATUS_PHASELOCK) | \ 8062306a36Sopenharmony_ci BIT(ADPLL_STATUS_FREQLOCK)) 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define ADPLL_M3DIV_OFFSET 0x28 /* Only on MPUPLL */ 8362306a36Sopenharmony_ci#define ADPLL_M3DIV_M3 0 8462306a36Sopenharmony_ci#define ADPLL_M3DIV_M3_WIDTH 5 8562306a36Sopenharmony_ci#define ADPLL_M3DIV_M3_MASK 0x1f 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define ADPLL_RAMPCTRL_OFFSET 0x2c /* Only on MPUPLL */ 8862306a36Sopenharmony_ci#define ADPLL_RAMPCTRL_CLKRAMPLEVEL 19 8962306a36Sopenharmony_ci#define ADPLL_RAMPCTRL_CLKRAMPRATE 16 9062306a36Sopenharmony_ci#define ADPLL_RAMPCTRL_RELOCK_RAMP_EN 0 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define MAX_ADPLL_INPUTS 3 9362306a36Sopenharmony_ci#define MAX_ADPLL_OUTPUTS 4 9462306a36Sopenharmony_ci#define ADPLL_MAX_RETRIES 5 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define to_dco(_hw) container_of(_hw, struct ti_adpll_dco_data, hw) 9762306a36Sopenharmony_ci#define to_adpll(_hw) container_of(_hw, struct ti_adpll_data, dco) 9862306a36Sopenharmony_ci#define to_clkout(_hw) container_of(_hw, struct ti_adpll_clkout_data, hw) 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cienum ti_adpll_clocks { 10162306a36Sopenharmony_ci TI_ADPLL_DCO, 10262306a36Sopenharmony_ci TI_ADPLL_DCO_GATE, 10362306a36Sopenharmony_ci TI_ADPLL_N2, 10462306a36Sopenharmony_ci TI_ADPLL_M2, 10562306a36Sopenharmony_ci TI_ADPLL_M2_GATE, 10662306a36Sopenharmony_ci TI_ADPLL_BYPASS, 10762306a36Sopenharmony_ci TI_ADPLL_HIF, 10862306a36Sopenharmony_ci TI_ADPLL_DIV2, 10962306a36Sopenharmony_ci TI_ADPLL_CLKOUT, 11062306a36Sopenharmony_ci TI_ADPLL_CLKOUT2, 11162306a36Sopenharmony_ci TI_ADPLL_M3, 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define TI_ADPLL_NR_CLOCKS (TI_ADPLL_M3 + 1) 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cienum ti_adpll_inputs { 11762306a36Sopenharmony_ci TI_ADPLL_CLKINP, 11862306a36Sopenharmony_ci TI_ADPLL_CLKINPULOW, 11962306a36Sopenharmony_ci TI_ADPLL_CLKINPHIF, 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cienum ti_adpll_s_outputs { 12362306a36Sopenharmony_ci TI_ADPLL_S_DCOCLKLDO, 12462306a36Sopenharmony_ci TI_ADPLL_S_CLKOUT, 12562306a36Sopenharmony_ci TI_ADPLL_S_CLKOUTX2, 12662306a36Sopenharmony_ci TI_ADPLL_S_CLKOUTHIF, 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cienum ti_adpll_lj_outputs { 13062306a36Sopenharmony_ci TI_ADPLL_LJ_CLKDCOLDO, 13162306a36Sopenharmony_ci TI_ADPLL_LJ_CLKOUT, 13262306a36Sopenharmony_ci TI_ADPLL_LJ_CLKOUTLDO, 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistruct ti_adpll_platform_data { 13662306a36Sopenharmony_ci const bool is_type_s; 13762306a36Sopenharmony_ci const int nr_max_inputs; 13862306a36Sopenharmony_ci const int nr_max_outputs; 13962306a36Sopenharmony_ci const int output_index; 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistruct ti_adpll_clock { 14362306a36Sopenharmony_ci struct clk *clk; 14462306a36Sopenharmony_ci struct clk_lookup *cl; 14562306a36Sopenharmony_ci void (*unregister)(struct clk *clk); 14662306a36Sopenharmony_ci}; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistruct ti_adpll_dco_data { 14962306a36Sopenharmony_ci struct clk_hw hw; 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistruct ti_adpll_clkout_data { 15362306a36Sopenharmony_ci struct ti_adpll_data *adpll; 15462306a36Sopenharmony_ci struct clk_gate gate; 15562306a36Sopenharmony_ci struct clk_hw hw; 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistruct ti_adpll_data { 15962306a36Sopenharmony_ci struct device *dev; 16062306a36Sopenharmony_ci const struct ti_adpll_platform_data *c; 16162306a36Sopenharmony_ci struct device_node *np; 16262306a36Sopenharmony_ci unsigned long pa; 16362306a36Sopenharmony_ci void __iomem *iobase; 16462306a36Sopenharmony_ci void __iomem *regs; 16562306a36Sopenharmony_ci spinlock_t lock; /* For ADPLL shared register access */ 16662306a36Sopenharmony_ci const char *parent_names[MAX_ADPLL_INPUTS]; 16762306a36Sopenharmony_ci struct clk *parent_clocks[MAX_ADPLL_INPUTS]; 16862306a36Sopenharmony_ci struct ti_adpll_clock *clocks; 16962306a36Sopenharmony_ci struct clk_onecell_data outputs; 17062306a36Sopenharmony_ci struct ti_adpll_dco_data dco; 17162306a36Sopenharmony_ci}; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic const char *ti_adpll_clk_get_name(struct ti_adpll_data *d, 17462306a36Sopenharmony_ci int output_index, 17562306a36Sopenharmony_ci const char *postfix) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci const char *name; 17862306a36Sopenharmony_ci int err; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (output_index >= 0) { 18162306a36Sopenharmony_ci err = of_property_read_string_index(d->np, 18262306a36Sopenharmony_ci "clock-output-names", 18362306a36Sopenharmony_ci output_index, 18462306a36Sopenharmony_ci &name); 18562306a36Sopenharmony_ci if (err) 18662306a36Sopenharmony_ci return NULL; 18762306a36Sopenharmony_ci } else { 18862306a36Sopenharmony_ci name = devm_kasprintf(d->dev, GFP_KERNEL, "%08lx.adpll.%s", 18962306a36Sopenharmony_ci d->pa, postfix); 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return name; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci#define ADPLL_MAX_CON_ID 16 /* See MAX_CON_ID */ 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int ti_adpll_setup_clock(struct ti_adpll_data *d, struct clk *clock, 19862306a36Sopenharmony_ci int index, int output_index, const char *name, 19962306a36Sopenharmony_ci void (*unregister)(struct clk *clk)) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct clk_lookup *cl; 20262306a36Sopenharmony_ci const char *postfix = NULL; 20362306a36Sopenharmony_ci char con_id[ADPLL_MAX_CON_ID]; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci d->clocks[index].clk = clock; 20662306a36Sopenharmony_ci d->clocks[index].unregister = unregister; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* Separate con_id in format "pll040dcoclkldo" to fit MAX_CON_ID */ 20962306a36Sopenharmony_ci postfix = strrchr(name, '.'); 21062306a36Sopenharmony_ci if (postfix && strlen(postfix) > 1) { 21162306a36Sopenharmony_ci if (strlen(postfix) > ADPLL_MAX_CON_ID) 21262306a36Sopenharmony_ci dev_warn(d->dev, "clock %s con_id lookup may fail\n", 21362306a36Sopenharmony_ci name); 21462306a36Sopenharmony_ci snprintf(con_id, 16, "pll%03lx%s", d->pa & 0xfff, postfix + 1); 21562306a36Sopenharmony_ci cl = clkdev_create(clock, con_id, NULL); 21662306a36Sopenharmony_ci if (!cl) 21762306a36Sopenharmony_ci return -ENOMEM; 21862306a36Sopenharmony_ci d->clocks[index].cl = cl; 21962306a36Sopenharmony_ci } else { 22062306a36Sopenharmony_ci dev_warn(d->dev, "no con_id for clock %s\n", name); 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (output_index < 0) 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci d->outputs.clks[output_index] = clock; 22762306a36Sopenharmony_ci d->outputs.clk_num++; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return 0; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic int ti_adpll_init_divider(struct ti_adpll_data *d, 23362306a36Sopenharmony_ci enum ti_adpll_clocks index, 23462306a36Sopenharmony_ci int output_index, char *name, 23562306a36Sopenharmony_ci struct clk *parent_clock, 23662306a36Sopenharmony_ci void __iomem *reg, 23762306a36Sopenharmony_ci u8 shift, u8 width, 23862306a36Sopenharmony_ci u8 clk_divider_flags) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci const char *child_name; 24162306a36Sopenharmony_ci const char *parent_name; 24262306a36Sopenharmony_ci struct clk *clock; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci child_name = ti_adpll_clk_get_name(d, output_index, name); 24562306a36Sopenharmony_ci if (!child_name) 24662306a36Sopenharmony_ci return -EINVAL; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci parent_name = __clk_get_name(parent_clock); 24962306a36Sopenharmony_ci clock = clk_register_divider(d->dev, child_name, parent_name, 0, 25062306a36Sopenharmony_ci reg, shift, width, clk_divider_flags, 25162306a36Sopenharmony_ci &d->lock); 25262306a36Sopenharmony_ci if (IS_ERR(clock)) { 25362306a36Sopenharmony_ci dev_err(d->dev, "failed to register divider %s: %li\n", 25462306a36Sopenharmony_ci name, PTR_ERR(clock)); 25562306a36Sopenharmony_ci return PTR_ERR(clock); 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci return ti_adpll_setup_clock(d, clock, index, output_index, child_name, 25962306a36Sopenharmony_ci clk_unregister_divider); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic int ti_adpll_init_mux(struct ti_adpll_data *d, 26362306a36Sopenharmony_ci enum ti_adpll_clocks index, 26462306a36Sopenharmony_ci char *name, struct clk *clk0, 26562306a36Sopenharmony_ci struct clk *clk1, 26662306a36Sopenharmony_ci void __iomem *reg, 26762306a36Sopenharmony_ci u8 shift) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci const char *child_name; 27062306a36Sopenharmony_ci const char *parents[2]; 27162306a36Sopenharmony_ci struct clk *clock; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci child_name = ti_adpll_clk_get_name(d, -ENODEV, name); 27462306a36Sopenharmony_ci if (!child_name) 27562306a36Sopenharmony_ci return -ENOMEM; 27662306a36Sopenharmony_ci parents[0] = __clk_get_name(clk0); 27762306a36Sopenharmony_ci parents[1] = __clk_get_name(clk1); 27862306a36Sopenharmony_ci clock = clk_register_mux(d->dev, child_name, parents, 2, 0, 27962306a36Sopenharmony_ci reg, shift, 1, 0, &d->lock); 28062306a36Sopenharmony_ci if (IS_ERR(clock)) { 28162306a36Sopenharmony_ci dev_err(d->dev, "failed to register mux %s: %li\n", 28262306a36Sopenharmony_ci name, PTR_ERR(clock)); 28362306a36Sopenharmony_ci return PTR_ERR(clock); 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci return ti_adpll_setup_clock(d, clock, index, -ENODEV, child_name, 28762306a36Sopenharmony_ci clk_unregister_mux); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic int ti_adpll_init_gate(struct ti_adpll_data *d, 29162306a36Sopenharmony_ci enum ti_adpll_clocks index, 29262306a36Sopenharmony_ci int output_index, char *name, 29362306a36Sopenharmony_ci struct clk *parent_clock, 29462306a36Sopenharmony_ci void __iomem *reg, 29562306a36Sopenharmony_ci u8 bit_idx, 29662306a36Sopenharmony_ci u8 clk_gate_flags) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci const char *child_name; 29962306a36Sopenharmony_ci const char *parent_name; 30062306a36Sopenharmony_ci struct clk *clock; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci child_name = ti_adpll_clk_get_name(d, output_index, name); 30362306a36Sopenharmony_ci if (!child_name) 30462306a36Sopenharmony_ci return -EINVAL; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci parent_name = __clk_get_name(parent_clock); 30762306a36Sopenharmony_ci clock = clk_register_gate(d->dev, child_name, parent_name, 0, 30862306a36Sopenharmony_ci reg, bit_idx, clk_gate_flags, 30962306a36Sopenharmony_ci &d->lock); 31062306a36Sopenharmony_ci if (IS_ERR(clock)) { 31162306a36Sopenharmony_ci dev_err(d->dev, "failed to register gate %s: %li\n", 31262306a36Sopenharmony_ci name, PTR_ERR(clock)); 31362306a36Sopenharmony_ci return PTR_ERR(clock); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return ti_adpll_setup_clock(d, clock, index, output_index, child_name, 31762306a36Sopenharmony_ci clk_unregister_gate); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic int ti_adpll_init_fixed_factor(struct ti_adpll_data *d, 32162306a36Sopenharmony_ci enum ti_adpll_clocks index, 32262306a36Sopenharmony_ci char *name, 32362306a36Sopenharmony_ci struct clk *parent_clock, 32462306a36Sopenharmony_ci unsigned int mult, 32562306a36Sopenharmony_ci unsigned int div) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci const char *child_name; 32862306a36Sopenharmony_ci const char *parent_name; 32962306a36Sopenharmony_ci struct clk *clock; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci child_name = ti_adpll_clk_get_name(d, -ENODEV, name); 33262306a36Sopenharmony_ci if (!child_name) 33362306a36Sopenharmony_ci return -ENOMEM; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci parent_name = __clk_get_name(parent_clock); 33662306a36Sopenharmony_ci clock = clk_register_fixed_factor(d->dev, child_name, parent_name, 33762306a36Sopenharmony_ci 0, mult, div); 33862306a36Sopenharmony_ci if (IS_ERR(clock)) 33962306a36Sopenharmony_ci return PTR_ERR(clock); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return ti_adpll_setup_clock(d, clock, index, -ENODEV, child_name, 34262306a36Sopenharmony_ci clk_unregister); 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic void ti_adpll_set_idle_bypass(struct ti_adpll_data *d) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci unsigned long flags; 34862306a36Sopenharmony_ci u32 v; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci spin_lock_irqsave(&d->lock, flags); 35162306a36Sopenharmony_ci v = readl_relaxed(d->regs + ADPLL_CLKCTRL_OFFSET); 35262306a36Sopenharmony_ci v |= BIT(ADPLL_CLKCTRL_IDLE); 35362306a36Sopenharmony_ci writel_relaxed(v, d->regs + ADPLL_CLKCTRL_OFFSET); 35462306a36Sopenharmony_ci spin_unlock_irqrestore(&d->lock, flags); 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic void ti_adpll_clear_idle_bypass(struct ti_adpll_data *d) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci unsigned long flags; 36062306a36Sopenharmony_ci u32 v; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci spin_lock_irqsave(&d->lock, flags); 36362306a36Sopenharmony_ci v = readl_relaxed(d->regs + ADPLL_CLKCTRL_OFFSET); 36462306a36Sopenharmony_ci v &= ~BIT(ADPLL_CLKCTRL_IDLE); 36562306a36Sopenharmony_ci writel_relaxed(v, d->regs + ADPLL_CLKCTRL_OFFSET); 36662306a36Sopenharmony_ci spin_unlock_irqrestore(&d->lock, flags); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic bool ti_adpll_clock_is_bypass(struct ti_adpll_data *d) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci u32 v; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci v = readl_relaxed(d->regs + ADPLL_STATUS_OFFSET); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci return v & BIT(ADPLL_STATUS_BYPASS); 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci/* 37962306a36Sopenharmony_ci * Locked and bypass are not actually mutually exclusive: if you only care 38062306a36Sopenharmony_ci * about the DCO clock and not CLKOUT you can clear M2PWDNZ before enabling 38162306a36Sopenharmony_ci * the PLL, resulting in status (FREQLOCK | PHASELOCK | BYPASS) after lock. 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_cistatic bool ti_adpll_is_locked(struct ti_adpll_data *d) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci u32 v = readl_relaxed(d->regs + ADPLL_STATUS_OFFSET); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci return (v & ADPLL_STATUS_PREPARED_MASK) == ADPLL_STATUS_PREPARED_MASK; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic int ti_adpll_wait_lock(struct ti_adpll_data *d) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci int retries = ADPLL_MAX_RETRIES; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci do { 39562306a36Sopenharmony_ci if (ti_adpll_is_locked(d)) 39662306a36Sopenharmony_ci return 0; 39762306a36Sopenharmony_ci usleep_range(200, 300); 39862306a36Sopenharmony_ci } while (retries--); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci dev_err(d->dev, "pll failed to lock\n"); 40162306a36Sopenharmony_ci return -ETIMEDOUT; 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic int ti_adpll_prepare(struct clk_hw *hw) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct ti_adpll_dco_data *dco = to_dco(hw); 40762306a36Sopenharmony_ci struct ti_adpll_data *d = to_adpll(dco); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci ti_adpll_clear_idle_bypass(d); 41062306a36Sopenharmony_ci ti_adpll_wait_lock(d); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci return 0; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void ti_adpll_unprepare(struct clk_hw *hw) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct ti_adpll_dco_data *dco = to_dco(hw); 41862306a36Sopenharmony_ci struct ti_adpll_data *d = to_adpll(dco); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci ti_adpll_set_idle_bypass(d); 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic int ti_adpll_is_prepared(struct clk_hw *hw) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct ti_adpll_dco_data *dco = to_dco(hw); 42662306a36Sopenharmony_ci struct ti_adpll_data *d = to_adpll(dco); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci return ti_adpll_is_locked(d); 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci/* 43262306a36Sopenharmony_ci * Note that the DCO clock is never subject to bypass: if the PLL is off, 43362306a36Sopenharmony_ci * dcoclk is low. 43462306a36Sopenharmony_ci */ 43562306a36Sopenharmony_cistatic unsigned long ti_adpll_recalc_rate(struct clk_hw *hw, 43662306a36Sopenharmony_ci unsigned long parent_rate) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct ti_adpll_dco_data *dco = to_dco(hw); 43962306a36Sopenharmony_ci struct ti_adpll_data *d = to_adpll(dco); 44062306a36Sopenharmony_ci u32 frac_m, divider, v; 44162306a36Sopenharmony_ci u64 rate; 44262306a36Sopenharmony_ci unsigned long flags; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (ti_adpll_clock_is_bypass(d)) 44562306a36Sopenharmony_ci return 0; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci spin_lock_irqsave(&d->lock, flags); 44862306a36Sopenharmony_ci frac_m = readl_relaxed(d->regs + ADPLL_FRACDIV_OFFSET); 44962306a36Sopenharmony_ci frac_m &= ADPLL_FRACDIV_FRACTIONALM_MASK; 45062306a36Sopenharmony_ci rate = (u64)readw_relaxed(d->regs + ADPLL_MN2DIV_OFFSET) << 18; 45162306a36Sopenharmony_ci rate += frac_m; 45262306a36Sopenharmony_ci rate *= parent_rate; 45362306a36Sopenharmony_ci divider = (readw_relaxed(d->regs + ADPLL_M2NDIV_OFFSET) + 1) << 18; 45462306a36Sopenharmony_ci spin_unlock_irqrestore(&d->lock, flags); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci do_div(rate, divider); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (d->c->is_type_s) { 45962306a36Sopenharmony_ci v = readl_relaxed(d->regs + ADPLL_CLKCTRL_OFFSET); 46062306a36Sopenharmony_ci if (v & BIT(ADPLL_CLKCTRL_REGM4XEN_ADPLL_S)) 46162306a36Sopenharmony_ci rate *= 4; 46262306a36Sopenharmony_ci rate *= 2; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci return rate; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci/* PLL parent is always clkinp, bypass only affects the children */ 46962306a36Sopenharmony_cistatic u8 ti_adpll_get_parent(struct clk_hw *hw) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci return 0; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic const struct clk_ops ti_adpll_ops = { 47562306a36Sopenharmony_ci .prepare = ti_adpll_prepare, 47662306a36Sopenharmony_ci .unprepare = ti_adpll_unprepare, 47762306a36Sopenharmony_ci .is_prepared = ti_adpll_is_prepared, 47862306a36Sopenharmony_ci .recalc_rate = ti_adpll_recalc_rate, 47962306a36Sopenharmony_ci .get_parent = ti_adpll_get_parent, 48062306a36Sopenharmony_ci}; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic int ti_adpll_init_dco(struct ti_adpll_data *d) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct clk_init_data init; 48562306a36Sopenharmony_ci struct clk *clock; 48662306a36Sopenharmony_ci const char *postfix; 48762306a36Sopenharmony_ci int width, err; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci d->outputs.clks = devm_kcalloc(d->dev, 49062306a36Sopenharmony_ci MAX_ADPLL_OUTPUTS, 49162306a36Sopenharmony_ci sizeof(struct clk *), 49262306a36Sopenharmony_ci GFP_KERNEL); 49362306a36Sopenharmony_ci if (!d->outputs.clks) 49462306a36Sopenharmony_ci return -ENOMEM; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (d->c->output_index < 0) 49762306a36Sopenharmony_ci postfix = "dco"; 49862306a36Sopenharmony_ci else 49962306a36Sopenharmony_ci postfix = NULL; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci init.name = ti_adpll_clk_get_name(d, d->c->output_index, postfix); 50262306a36Sopenharmony_ci if (!init.name) 50362306a36Sopenharmony_ci return -EINVAL; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci init.parent_names = d->parent_names; 50662306a36Sopenharmony_ci init.num_parents = d->c->nr_max_inputs; 50762306a36Sopenharmony_ci init.ops = &ti_adpll_ops; 50862306a36Sopenharmony_ci init.flags = CLK_GET_RATE_NOCACHE; 50962306a36Sopenharmony_ci d->dco.hw.init = &init; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (d->c->is_type_s) 51262306a36Sopenharmony_ci width = 5; 51362306a36Sopenharmony_ci else 51462306a36Sopenharmony_ci width = 4; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* Internal input clock divider N2 */ 51762306a36Sopenharmony_ci err = ti_adpll_init_divider(d, TI_ADPLL_N2, -ENODEV, "n2", 51862306a36Sopenharmony_ci d->parent_clocks[TI_ADPLL_CLKINP], 51962306a36Sopenharmony_ci d->regs + ADPLL_MN2DIV_OFFSET, 52062306a36Sopenharmony_ci ADPLL_MN2DIV_N2, width, 0); 52162306a36Sopenharmony_ci if (err) 52262306a36Sopenharmony_ci return err; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci clock = devm_clk_register(d->dev, &d->dco.hw); 52562306a36Sopenharmony_ci if (IS_ERR(clock)) 52662306a36Sopenharmony_ci return PTR_ERR(clock); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci return ti_adpll_setup_clock(d, clock, TI_ADPLL_DCO, d->c->output_index, 52962306a36Sopenharmony_ci init.name, NULL); 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic int ti_adpll_clkout_enable(struct clk_hw *hw) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct ti_adpll_clkout_data *co = to_clkout(hw); 53562306a36Sopenharmony_ci struct clk_hw *gate_hw = &co->gate.hw; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci __clk_hw_set_clk(gate_hw, hw); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return clk_gate_ops.enable(gate_hw); 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic void ti_adpll_clkout_disable(struct clk_hw *hw) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct ti_adpll_clkout_data *co = to_clkout(hw); 54562306a36Sopenharmony_ci struct clk_hw *gate_hw = &co->gate.hw; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci __clk_hw_set_clk(gate_hw, hw); 54862306a36Sopenharmony_ci clk_gate_ops.disable(gate_hw); 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic int ti_adpll_clkout_is_enabled(struct clk_hw *hw) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct ti_adpll_clkout_data *co = to_clkout(hw); 55462306a36Sopenharmony_ci struct clk_hw *gate_hw = &co->gate.hw; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci __clk_hw_set_clk(gate_hw, hw); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci return clk_gate_ops.is_enabled(gate_hw); 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci/* Setting PLL bypass puts clkout and clkoutx2 into bypass */ 56262306a36Sopenharmony_cistatic u8 ti_adpll_clkout_get_parent(struct clk_hw *hw) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci struct ti_adpll_clkout_data *co = to_clkout(hw); 56562306a36Sopenharmony_ci struct ti_adpll_data *d = co->adpll; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci return ti_adpll_clock_is_bypass(d); 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistatic int ti_adpll_init_clkout(struct ti_adpll_data *d, 57162306a36Sopenharmony_ci enum ti_adpll_clocks index, 57262306a36Sopenharmony_ci int output_index, int gate_bit, 57362306a36Sopenharmony_ci char *name, struct clk *clk0, 57462306a36Sopenharmony_ci struct clk *clk1) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct ti_adpll_clkout_data *co; 57762306a36Sopenharmony_ci struct clk_init_data init; 57862306a36Sopenharmony_ci struct clk_ops *ops; 57962306a36Sopenharmony_ci const char *parent_names[2]; 58062306a36Sopenharmony_ci const char *child_name; 58162306a36Sopenharmony_ci struct clk *clock; 58262306a36Sopenharmony_ci int err; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci co = devm_kzalloc(d->dev, sizeof(*co), GFP_KERNEL); 58562306a36Sopenharmony_ci if (!co) 58662306a36Sopenharmony_ci return -ENOMEM; 58762306a36Sopenharmony_ci co->adpll = d; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci err = of_property_read_string_index(d->np, 59062306a36Sopenharmony_ci "clock-output-names", 59162306a36Sopenharmony_ci output_index, 59262306a36Sopenharmony_ci &child_name); 59362306a36Sopenharmony_ci if (err) 59462306a36Sopenharmony_ci return err; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci ops = devm_kzalloc(d->dev, sizeof(*ops), GFP_KERNEL); 59762306a36Sopenharmony_ci if (!ops) 59862306a36Sopenharmony_ci return -ENOMEM; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci init.name = child_name; 60162306a36Sopenharmony_ci init.ops = ops; 60262306a36Sopenharmony_ci init.flags = 0; 60362306a36Sopenharmony_ci co->hw.init = &init; 60462306a36Sopenharmony_ci parent_names[0] = __clk_get_name(clk0); 60562306a36Sopenharmony_ci parent_names[1] = __clk_get_name(clk1); 60662306a36Sopenharmony_ci init.parent_names = parent_names; 60762306a36Sopenharmony_ci init.num_parents = 2; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci ops->get_parent = ti_adpll_clkout_get_parent; 61062306a36Sopenharmony_ci ops->determine_rate = __clk_mux_determine_rate; 61162306a36Sopenharmony_ci if (gate_bit) { 61262306a36Sopenharmony_ci co->gate.lock = &d->lock; 61362306a36Sopenharmony_ci co->gate.reg = d->regs + ADPLL_CLKCTRL_OFFSET; 61462306a36Sopenharmony_ci co->gate.bit_idx = gate_bit; 61562306a36Sopenharmony_ci ops->enable = ti_adpll_clkout_enable; 61662306a36Sopenharmony_ci ops->disable = ti_adpll_clkout_disable; 61762306a36Sopenharmony_ci ops->is_enabled = ti_adpll_clkout_is_enabled; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci clock = devm_clk_register(d->dev, &co->hw); 62162306a36Sopenharmony_ci if (IS_ERR(clock)) { 62262306a36Sopenharmony_ci dev_err(d->dev, "failed to register output %s: %li\n", 62362306a36Sopenharmony_ci name, PTR_ERR(clock)); 62462306a36Sopenharmony_ci return PTR_ERR(clock); 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci return ti_adpll_setup_clock(d, clock, index, output_index, child_name, 62862306a36Sopenharmony_ci NULL); 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic int ti_adpll_init_children_adpll_s(struct ti_adpll_data *d) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci int err; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (!d->c->is_type_s) 63662306a36Sopenharmony_ci return 0; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci /* Internal mux, sources from divider N2 or clkinpulow */ 63962306a36Sopenharmony_ci err = ti_adpll_init_mux(d, TI_ADPLL_BYPASS, "bypass", 64062306a36Sopenharmony_ci d->clocks[TI_ADPLL_N2].clk, 64162306a36Sopenharmony_ci d->parent_clocks[TI_ADPLL_CLKINPULOW], 64262306a36Sopenharmony_ci d->regs + ADPLL_CLKCTRL_OFFSET, 64362306a36Sopenharmony_ci ADPLL_CLKCTRL_ULOWCLKEN); 64462306a36Sopenharmony_ci if (err) 64562306a36Sopenharmony_ci return err; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* Internal divider M2, sources DCO */ 64862306a36Sopenharmony_ci err = ti_adpll_init_divider(d, TI_ADPLL_M2, -ENODEV, "m2", 64962306a36Sopenharmony_ci d->clocks[TI_ADPLL_DCO].clk, 65062306a36Sopenharmony_ci d->regs + ADPLL_M2NDIV_OFFSET, 65162306a36Sopenharmony_ci ADPLL_M2NDIV_M2, 65262306a36Sopenharmony_ci ADPLL_M2NDIV_M2_ADPLL_S_WIDTH, 65362306a36Sopenharmony_ci CLK_DIVIDER_ONE_BASED); 65462306a36Sopenharmony_ci if (err) 65562306a36Sopenharmony_ci return err; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci /* Internal fixed divider, after M2 before clkout */ 65862306a36Sopenharmony_ci err = ti_adpll_init_fixed_factor(d, TI_ADPLL_DIV2, "div2", 65962306a36Sopenharmony_ci d->clocks[TI_ADPLL_M2].clk, 66062306a36Sopenharmony_ci 1, 2); 66162306a36Sopenharmony_ci if (err) 66262306a36Sopenharmony_ci return err; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci /* Output clkout with a mux and gate, sources from div2 or bypass */ 66562306a36Sopenharmony_ci err = ti_adpll_init_clkout(d, TI_ADPLL_CLKOUT, TI_ADPLL_S_CLKOUT, 66662306a36Sopenharmony_ci ADPLL_CLKCTRL_CLKOUTEN, "clkout", 66762306a36Sopenharmony_ci d->clocks[TI_ADPLL_DIV2].clk, 66862306a36Sopenharmony_ci d->clocks[TI_ADPLL_BYPASS].clk); 66962306a36Sopenharmony_ci if (err) 67062306a36Sopenharmony_ci return err; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci /* Output clkoutx2 with a mux and gate, sources from M2 or bypass */ 67362306a36Sopenharmony_ci err = ti_adpll_init_clkout(d, TI_ADPLL_CLKOUT2, TI_ADPLL_S_CLKOUTX2, 0, 67462306a36Sopenharmony_ci "clkout2", d->clocks[TI_ADPLL_M2].clk, 67562306a36Sopenharmony_ci d->clocks[TI_ADPLL_BYPASS].clk); 67662306a36Sopenharmony_ci if (err) 67762306a36Sopenharmony_ci return err; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* Internal mux, sources from DCO and clkinphif */ 68062306a36Sopenharmony_ci if (d->parent_clocks[TI_ADPLL_CLKINPHIF]) { 68162306a36Sopenharmony_ci err = ti_adpll_init_mux(d, TI_ADPLL_HIF, "hif", 68262306a36Sopenharmony_ci d->clocks[TI_ADPLL_DCO].clk, 68362306a36Sopenharmony_ci d->parent_clocks[TI_ADPLL_CLKINPHIF], 68462306a36Sopenharmony_ci d->regs + ADPLL_CLKCTRL_OFFSET, 68562306a36Sopenharmony_ci ADPLL_CLKINPHIFSEL_ADPLL_S); 68662306a36Sopenharmony_ci if (err) 68762306a36Sopenharmony_ci return err; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* Output clkouthif with a divider M3, sources from hif */ 69162306a36Sopenharmony_ci err = ti_adpll_init_divider(d, TI_ADPLL_M3, TI_ADPLL_S_CLKOUTHIF, "m3", 69262306a36Sopenharmony_ci d->clocks[TI_ADPLL_HIF].clk, 69362306a36Sopenharmony_ci d->regs + ADPLL_M3DIV_OFFSET, 69462306a36Sopenharmony_ci ADPLL_M3DIV_M3, 69562306a36Sopenharmony_ci ADPLL_M3DIV_M3_WIDTH, 69662306a36Sopenharmony_ci CLK_DIVIDER_ONE_BASED); 69762306a36Sopenharmony_ci if (err) 69862306a36Sopenharmony_ci return err; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci /* Output clock dcoclkldo is the DCO */ 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci return 0; 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic int ti_adpll_init_children_adpll_lj(struct ti_adpll_data *d) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci int err; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (d->c->is_type_s) 71062306a36Sopenharmony_ci return 0; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* Output clkdcoldo, gated output of DCO */ 71362306a36Sopenharmony_ci err = ti_adpll_init_gate(d, TI_ADPLL_DCO_GATE, TI_ADPLL_LJ_CLKDCOLDO, 71462306a36Sopenharmony_ci "clkdcoldo", d->clocks[TI_ADPLL_DCO].clk, 71562306a36Sopenharmony_ci d->regs + ADPLL_CLKCTRL_OFFSET, 71662306a36Sopenharmony_ci ADPLL_CLKCTRL_CLKDCOLDOEN, 0); 71762306a36Sopenharmony_ci if (err) 71862306a36Sopenharmony_ci return err; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* Internal divider M2, sources from DCO */ 72162306a36Sopenharmony_ci err = ti_adpll_init_divider(d, TI_ADPLL_M2, -ENODEV, 72262306a36Sopenharmony_ci "m2", d->clocks[TI_ADPLL_DCO].clk, 72362306a36Sopenharmony_ci d->regs + ADPLL_M2NDIV_OFFSET, 72462306a36Sopenharmony_ci ADPLL_M2NDIV_M2, 72562306a36Sopenharmony_ci ADPLL_M2NDIV_M2_ADPLL_LJ_WIDTH, 72662306a36Sopenharmony_ci CLK_DIVIDER_ONE_BASED); 72762306a36Sopenharmony_ci if (err) 72862306a36Sopenharmony_ci return err; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* Output clkoutldo, gated output of M2 */ 73162306a36Sopenharmony_ci err = ti_adpll_init_gate(d, TI_ADPLL_M2_GATE, TI_ADPLL_LJ_CLKOUTLDO, 73262306a36Sopenharmony_ci "clkoutldo", d->clocks[TI_ADPLL_M2].clk, 73362306a36Sopenharmony_ci d->regs + ADPLL_CLKCTRL_OFFSET, 73462306a36Sopenharmony_ci ADPLL_CLKCTRL_CLKOUTLDOEN_ADPLL_LJ, 73562306a36Sopenharmony_ci 0); 73662306a36Sopenharmony_ci if (err) 73762306a36Sopenharmony_ci return err; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci /* Internal mux, sources from divider N2 or clkinpulow */ 74062306a36Sopenharmony_ci err = ti_adpll_init_mux(d, TI_ADPLL_BYPASS, "bypass", 74162306a36Sopenharmony_ci d->clocks[TI_ADPLL_N2].clk, 74262306a36Sopenharmony_ci d->parent_clocks[TI_ADPLL_CLKINPULOW], 74362306a36Sopenharmony_ci d->regs + ADPLL_CLKCTRL_OFFSET, 74462306a36Sopenharmony_ci ADPLL_CLKCTRL_ULOWCLKEN); 74562306a36Sopenharmony_ci if (err) 74662306a36Sopenharmony_ci return err; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci /* Output clkout, sources M2 or bypass */ 74962306a36Sopenharmony_ci err = ti_adpll_init_clkout(d, TI_ADPLL_CLKOUT, TI_ADPLL_S_CLKOUT, 75062306a36Sopenharmony_ci ADPLL_CLKCTRL_CLKOUTEN, "clkout", 75162306a36Sopenharmony_ci d->clocks[TI_ADPLL_M2].clk, 75262306a36Sopenharmony_ci d->clocks[TI_ADPLL_BYPASS].clk); 75362306a36Sopenharmony_ci if (err) 75462306a36Sopenharmony_ci return err; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci return 0; 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic void ti_adpll_free_resources(struct ti_adpll_data *d) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci int i; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci for (i = TI_ADPLL_M3; i >= 0; i--) { 76462306a36Sopenharmony_ci struct ti_adpll_clock *ac = &d->clocks[i]; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (!ac || IS_ERR_OR_NULL(ac->clk)) 76762306a36Sopenharmony_ci continue; 76862306a36Sopenharmony_ci if (ac->cl) 76962306a36Sopenharmony_ci clkdev_drop(ac->cl); 77062306a36Sopenharmony_ci if (ac->unregister) 77162306a36Sopenharmony_ci ac->unregister(ac->clk); 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci/* MPU PLL manages the lock register for all PLLs */ 77662306a36Sopenharmony_cistatic void ti_adpll_unlock_all(void __iomem *reg) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci u32 v; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci v = readl_relaxed(reg); 78162306a36Sopenharmony_ci if (v == ADPLL_PLLSS_MMR_LOCK_ENABLED) 78262306a36Sopenharmony_ci writel_relaxed(ADPLL_PLLSS_MMR_UNLOCK_MAGIC, reg); 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistatic int ti_adpll_init_registers(struct ti_adpll_data *d) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci int register_offset = 0; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (d->c->is_type_s) { 79062306a36Sopenharmony_ci register_offset = 8; 79162306a36Sopenharmony_ci ti_adpll_unlock_all(d->iobase + ADPLL_PLLSS_MMR_LOCK_OFFSET); 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci d->regs = d->iobase + register_offset + ADPLL_PWRCTRL_OFFSET; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci return 0; 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic int ti_adpll_init_inputs(struct ti_adpll_data *d) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci static const char error[] = "need at least %i inputs"; 80262306a36Sopenharmony_ci struct clk *clock; 80362306a36Sopenharmony_ci int nr_inputs; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci nr_inputs = of_clk_get_parent_count(d->np); 80662306a36Sopenharmony_ci if (nr_inputs < d->c->nr_max_inputs) { 80762306a36Sopenharmony_ci dev_err(d->dev, error, nr_inputs); 80862306a36Sopenharmony_ci return -EINVAL; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci of_clk_parent_fill(d->np, d->parent_names, nr_inputs); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci clock = devm_clk_get(d->dev, d->parent_names[0]); 81362306a36Sopenharmony_ci if (IS_ERR(clock)) { 81462306a36Sopenharmony_ci dev_err(d->dev, "could not get clkinp\n"); 81562306a36Sopenharmony_ci return PTR_ERR(clock); 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci d->parent_clocks[TI_ADPLL_CLKINP] = clock; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci clock = devm_clk_get(d->dev, d->parent_names[1]); 82062306a36Sopenharmony_ci if (IS_ERR(clock)) { 82162306a36Sopenharmony_ci dev_err(d->dev, "could not get clkinpulow clock\n"); 82262306a36Sopenharmony_ci return PTR_ERR(clock); 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci d->parent_clocks[TI_ADPLL_CLKINPULOW] = clock; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (d->c->is_type_s) { 82762306a36Sopenharmony_ci clock = devm_clk_get(d->dev, d->parent_names[2]); 82862306a36Sopenharmony_ci if (IS_ERR(clock)) { 82962306a36Sopenharmony_ci dev_err(d->dev, "could not get clkinphif clock\n"); 83062306a36Sopenharmony_ci return PTR_ERR(clock); 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci d->parent_clocks[TI_ADPLL_CLKINPHIF] = clock; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci return 0; 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cistatic const struct ti_adpll_platform_data ti_adpll_type_s = { 83962306a36Sopenharmony_ci .is_type_s = true, 84062306a36Sopenharmony_ci .nr_max_inputs = MAX_ADPLL_INPUTS, 84162306a36Sopenharmony_ci .nr_max_outputs = MAX_ADPLL_OUTPUTS, 84262306a36Sopenharmony_ci .output_index = TI_ADPLL_S_DCOCLKLDO, 84362306a36Sopenharmony_ci}; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_cistatic const struct ti_adpll_platform_data ti_adpll_type_lj = { 84662306a36Sopenharmony_ci .is_type_s = false, 84762306a36Sopenharmony_ci .nr_max_inputs = MAX_ADPLL_INPUTS - 1, 84862306a36Sopenharmony_ci .nr_max_outputs = MAX_ADPLL_OUTPUTS - 1, 84962306a36Sopenharmony_ci .output_index = -EINVAL, 85062306a36Sopenharmony_ci}; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cistatic const struct of_device_id ti_adpll_match[] = { 85362306a36Sopenharmony_ci { .compatible = "ti,dm814-adpll-s-clock", &ti_adpll_type_s }, 85462306a36Sopenharmony_ci { .compatible = "ti,dm814-adpll-lj-clock", &ti_adpll_type_lj }, 85562306a36Sopenharmony_ci {}, 85662306a36Sopenharmony_ci}; 85762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ti_adpll_match); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic int ti_adpll_probe(struct platform_device *pdev) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 86262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 86362306a36Sopenharmony_ci const struct of_device_id *match; 86462306a36Sopenharmony_ci const struct ti_adpll_platform_data *pdata; 86562306a36Sopenharmony_ci struct ti_adpll_data *d; 86662306a36Sopenharmony_ci struct resource *res; 86762306a36Sopenharmony_ci int err; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci match = of_match_device(ti_adpll_match, dev); 87062306a36Sopenharmony_ci if (match) 87162306a36Sopenharmony_ci pdata = match->data; 87262306a36Sopenharmony_ci else 87362306a36Sopenharmony_ci return -ENODEV; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL); 87662306a36Sopenharmony_ci if (!d) 87762306a36Sopenharmony_ci return -ENOMEM; 87862306a36Sopenharmony_ci d->dev = dev; 87962306a36Sopenharmony_ci d->np = node; 88062306a36Sopenharmony_ci d->c = pdata; 88162306a36Sopenharmony_ci dev_set_drvdata(d->dev, d); 88262306a36Sopenharmony_ci spin_lock_init(&d->lock); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci d->iobase = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 88562306a36Sopenharmony_ci if (IS_ERR(d->iobase)) 88662306a36Sopenharmony_ci return PTR_ERR(d->iobase); 88762306a36Sopenharmony_ci d->pa = res->start; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci err = ti_adpll_init_registers(d); 89062306a36Sopenharmony_ci if (err) 89162306a36Sopenharmony_ci return err; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci err = ti_adpll_init_inputs(d); 89462306a36Sopenharmony_ci if (err) 89562306a36Sopenharmony_ci return err; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci d->clocks = devm_kcalloc(d->dev, 89862306a36Sopenharmony_ci TI_ADPLL_NR_CLOCKS, 89962306a36Sopenharmony_ci sizeof(struct ti_adpll_clock), 90062306a36Sopenharmony_ci GFP_KERNEL); 90162306a36Sopenharmony_ci if (!d->clocks) 90262306a36Sopenharmony_ci return -ENOMEM; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci err = ti_adpll_init_dco(d); 90562306a36Sopenharmony_ci if (err) { 90662306a36Sopenharmony_ci dev_err(dev, "could not register dco: %i\n", err); 90762306a36Sopenharmony_ci goto free; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci err = ti_adpll_init_children_adpll_s(d); 91162306a36Sopenharmony_ci if (err) 91262306a36Sopenharmony_ci goto free; 91362306a36Sopenharmony_ci err = ti_adpll_init_children_adpll_lj(d); 91462306a36Sopenharmony_ci if (err) 91562306a36Sopenharmony_ci goto free; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci err = of_clk_add_provider(d->np, of_clk_src_onecell_get, &d->outputs); 91862306a36Sopenharmony_ci if (err) 91962306a36Sopenharmony_ci goto free; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci return 0; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_cifree: 92462306a36Sopenharmony_ci WARN_ON(1); 92562306a36Sopenharmony_ci ti_adpll_free_resources(d); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci return err; 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cistatic void ti_adpll_remove(struct platform_device *pdev) 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci struct ti_adpll_data *d = dev_get_drvdata(&pdev->dev); 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci ti_adpll_free_resources(d); 93562306a36Sopenharmony_ci} 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_cistatic struct platform_driver ti_adpll_driver = { 93862306a36Sopenharmony_ci .driver = { 93962306a36Sopenharmony_ci .name = "ti-adpll", 94062306a36Sopenharmony_ci .of_match_table = ti_adpll_match, 94162306a36Sopenharmony_ci }, 94262306a36Sopenharmony_ci .probe = ti_adpll_probe, 94362306a36Sopenharmony_ci .remove_new = ti_adpll_remove, 94462306a36Sopenharmony_ci}; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic int __init ti_adpll_init(void) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci return platform_driver_register(&ti_adpll_driver); 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_cicore_initcall(ti_adpll_init); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_cistatic void __exit ti_adpll_exit(void) 95362306a36Sopenharmony_ci{ 95462306a36Sopenharmony_ci platform_driver_unregister(&ti_adpll_driver); 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_cimodule_exit(ti_adpll_exit); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ciMODULE_DESCRIPTION("Clock driver for dm814x ADPLL"); 95962306a36Sopenharmony_ciMODULE_ALIAS("platform:dm814-adpll-clock"); 96062306a36Sopenharmony_ciMODULE_AUTHOR("Tony LIndgren <tony@atomide.com>"); 96162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 962