162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// Copyright(c) 2015-17 Intel Corporation 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci/* 562306a36Sopenharmony_ci * skl-ssp-clk.c - ASoC skylake ssp clock driver 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci#include <linux/clk-provider.h> 1362306a36Sopenharmony_ci#include <linux/clkdev.h> 1462306a36Sopenharmony_ci#include <sound/intel-nhlt.h> 1562306a36Sopenharmony_ci#include "skl.h" 1662306a36Sopenharmony_ci#include "skl-ssp-clk.h" 1762306a36Sopenharmony_ci#include "skl-topology.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define to_skl_clk(_hw) container_of(_hw, struct skl_clk, hw) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct skl_clk_parent { 2262306a36Sopenharmony_ci struct clk_hw *hw; 2362306a36Sopenharmony_ci struct clk_lookup *lookup; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct skl_clk { 2762306a36Sopenharmony_ci struct clk_hw hw; 2862306a36Sopenharmony_ci struct clk_lookup *lookup; 2962306a36Sopenharmony_ci unsigned long rate; 3062306a36Sopenharmony_ci struct skl_clk_pdata *pdata; 3162306a36Sopenharmony_ci u32 id; 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct skl_clk_data { 3562306a36Sopenharmony_ci struct skl_clk_parent parent[SKL_MAX_CLK_SRC]; 3662306a36Sopenharmony_ci struct skl_clk *clk[SKL_MAX_CLK_CNT]; 3762306a36Sopenharmony_ci u8 avail_clk_cnt; 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int skl_get_clk_type(u32 index) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci switch (index) { 4362306a36Sopenharmony_ci case 0 ... (SKL_SCLK_OFS - 1): 4462306a36Sopenharmony_ci return SKL_MCLK; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci case SKL_SCLK_OFS ... (SKL_SCLKFS_OFS - 1): 4762306a36Sopenharmony_ci return SKL_SCLK; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci case SKL_SCLKFS_OFS ... (SKL_MAX_CLK_CNT - 1): 5062306a36Sopenharmony_ci return SKL_SCLK_FS; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci default: 5362306a36Sopenharmony_ci return -EINVAL; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int skl_get_vbus_id(u32 index, u8 clk_type) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci switch (clk_type) { 6062306a36Sopenharmony_ci case SKL_MCLK: 6162306a36Sopenharmony_ci return index; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci case SKL_SCLK: 6462306a36Sopenharmony_ci return index - SKL_SCLK_OFS; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci case SKL_SCLK_FS: 6762306a36Sopenharmony_ci return index - SKL_SCLKFS_OFS; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci default: 7062306a36Sopenharmony_ci return -EINVAL; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void skl_fill_clk_ipc(struct skl_clk_rate_cfg_table *rcfg, u8 clk_type) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct nhlt_fmt_cfg *fmt_cfg; 7762306a36Sopenharmony_ci union skl_clk_ctrl_ipc *ipc; 7862306a36Sopenharmony_ci struct wav_fmt *wfmt; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (!rcfg) 8162306a36Sopenharmony_ci return; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci ipc = &rcfg->dma_ctl_ipc; 8462306a36Sopenharmony_ci if (clk_type == SKL_SCLK_FS) { 8562306a36Sopenharmony_ci fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config; 8662306a36Sopenharmony_ci wfmt = &fmt_cfg->fmt_ext.fmt; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* Remove TLV Header size */ 8962306a36Sopenharmony_ci ipc->sclk_fs.hdr.size = sizeof(struct skl_dmactrl_sclkfs_cfg) - 9062306a36Sopenharmony_ci sizeof(struct skl_tlv_hdr); 9162306a36Sopenharmony_ci ipc->sclk_fs.sampling_frequency = wfmt->samples_per_sec; 9262306a36Sopenharmony_ci ipc->sclk_fs.bit_depth = wfmt->bits_per_sample; 9362306a36Sopenharmony_ci ipc->sclk_fs.valid_bit_depth = 9462306a36Sopenharmony_ci fmt_cfg->fmt_ext.sample.valid_bits_per_sample; 9562306a36Sopenharmony_ci ipc->sclk_fs.number_of_channels = wfmt->channels; 9662306a36Sopenharmony_ci } else { 9762306a36Sopenharmony_ci ipc->mclk.hdr.type = DMA_CLK_CONTROLS; 9862306a36Sopenharmony_ci /* Remove TLV Header size */ 9962306a36Sopenharmony_ci ipc->mclk.hdr.size = sizeof(struct skl_dmactrl_mclk_cfg) - 10062306a36Sopenharmony_ci sizeof(struct skl_tlv_hdr); 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* Sends dma control IPC to turn the clock ON/OFF */ 10562306a36Sopenharmony_cistatic int skl_send_clk_dma_control(struct skl_dev *skl, 10662306a36Sopenharmony_ci struct skl_clk_rate_cfg_table *rcfg, 10762306a36Sopenharmony_ci u32 vbus_id, u8 clk_type, 10862306a36Sopenharmony_ci bool enable) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct nhlt_specific_cfg *sp_cfg; 11162306a36Sopenharmony_ci u32 i2s_config_size, node_id = 0; 11262306a36Sopenharmony_ci struct nhlt_fmt_cfg *fmt_cfg; 11362306a36Sopenharmony_ci union skl_clk_ctrl_ipc *ipc; 11462306a36Sopenharmony_ci void *i2s_config = NULL; 11562306a36Sopenharmony_ci u8 *data, size; 11662306a36Sopenharmony_ci int ret; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (!rcfg) 11962306a36Sopenharmony_ci return -EIO; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci ipc = &rcfg->dma_ctl_ipc; 12262306a36Sopenharmony_ci fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config; 12362306a36Sopenharmony_ci sp_cfg = &fmt_cfg->config; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (clk_type == SKL_SCLK_FS) { 12662306a36Sopenharmony_ci ipc->sclk_fs.hdr.type = 12762306a36Sopenharmony_ci enable ? DMA_TRANSMITION_START : DMA_TRANSMITION_STOP; 12862306a36Sopenharmony_ci data = (u8 *)&ipc->sclk_fs; 12962306a36Sopenharmony_ci size = sizeof(struct skl_dmactrl_sclkfs_cfg); 13062306a36Sopenharmony_ci } else { 13162306a36Sopenharmony_ci /* 1 to enable mclk, 0 to enable sclk */ 13262306a36Sopenharmony_ci if (clk_type == SKL_SCLK) 13362306a36Sopenharmony_ci ipc->mclk.mclk = 0; 13462306a36Sopenharmony_ci else 13562306a36Sopenharmony_ci ipc->mclk.mclk = 1; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci ipc->mclk.keep_running = enable; 13862306a36Sopenharmony_ci ipc->mclk.warm_up_over = enable; 13962306a36Sopenharmony_ci ipc->mclk.clk_stop_over = !enable; 14062306a36Sopenharmony_ci data = (u8 *)&ipc->mclk; 14162306a36Sopenharmony_ci size = sizeof(struct skl_dmactrl_mclk_cfg); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci i2s_config_size = sp_cfg->size + size; 14562306a36Sopenharmony_ci i2s_config = kzalloc(i2s_config_size, GFP_KERNEL); 14662306a36Sopenharmony_ci if (!i2s_config) 14762306a36Sopenharmony_ci return -ENOMEM; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* copy blob */ 15062306a36Sopenharmony_ci memcpy(i2s_config, sp_cfg->caps, sp_cfg->size); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* copy additional dma controls information */ 15362306a36Sopenharmony_ci memcpy(i2s_config + sp_cfg->size, data, size); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci node_id = ((SKL_DMA_I2S_LINK_INPUT_CLASS << 8) | (vbus_id << 4)); 15662306a36Sopenharmony_ci ret = skl_dsp_set_dma_control(skl, (u32 *)i2s_config, 15762306a36Sopenharmony_ci i2s_config_size, node_id); 15862306a36Sopenharmony_ci kfree(i2s_config); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci return ret; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic struct skl_clk_rate_cfg_table *skl_get_rate_cfg( 16462306a36Sopenharmony_ci struct skl_clk_rate_cfg_table *rcfg, 16562306a36Sopenharmony_ci unsigned long rate) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci int i; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci for (i = 0; (i < SKL_MAX_CLK_RATES) && rcfg[i].rate; i++) { 17062306a36Sopenharmony_ci if (rcfg[i].rate == rate) 17162306a36Sopenharmony_ci return &rcfg[i]; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return NULL; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic int skl_clk_change_status(struct skl_clk *clkdev, 17862306a36Sopenharmony_ci bool enable) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct skl_clk_rate_cfg_table *rcfg; 18162306a36Sopenharmony_ci int vbus_id, clk_type; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci clk_type = skl_get_clk_type(clkdev->id); 18462306a36Sopenharmony_ci if (clk_type < 0) 18562306a36Sopenharmony_ci return clk_type; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci vbus_id = skl_get_vbus_id(clkdev->id, clk_type); 18862306a36Sopenharmony_ci if (vbus_id < 0) 18962306a36Sopenharmony_ci return vbus_id; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg, 19262306a36Sopenharmony_ci clkdev->rate); 19362306a36Sopenharmony_ci if (!rcfg) 19462306a36Sopenharmony_ci return -EINVAL; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return skl_send_clk_dma_control(clkdev->pdata->pvt_data, rcfg, 19762306a36Sopenharmony_ci vbus_id, clk_type, enable); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic int skl_clk_prepare(struct clk_hw *hw) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct skl_clk *clkdev = to_skl_clk(hw); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return skl_clk_change_status(clkdev, true); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic void skl_clk_unprepare(struct clk_hw *hw) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct skl_clk *clkdev = to_skl_clk(hw); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci skl_clk_change_status(clkdev, false); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic int skl_clk_set_rate(struct clk_hw *hw, unsigned long rate, 21562306a36Sopenharmony_ci unsigned long parent_rate) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct skl_clk *clkdev = to_skl_clk(hw); 21862306a36Sopenharmony_ci struct skl_clk_rate_cfg_table *rcfg; 21962306a36Sopenharmony_ci int clk_type; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (!rate) 22262306a36Sopenharmony_ci return -EINVAL; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg, 22562306a36Sopenharmony_ci rate); 22662306a36Sopenharmony_ci if (!rcfg) 22762306a36Sopenharmony_ci return -EINVAL; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci clk_type = skl_get_clk_type(clkdev->id); 23062306a36Sopenharmony_ci if (clk_type < 0) 23162306a36Sopenharmony_ci return clk_type; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci skl_fill_clk_ipc(rcfg, clk_type); 23462306a36Sopenharmony_ci clkdev->rate = rate; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic unsigned long skl_clk_recalc_rate(struct clk_hw *hw, 24062306a36Sopenharmony_ci unsigned long parent_rate) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct skl_clk *clkdev = to_skl_clk(hw); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (clkdev->rate) 24562306a36Sopenharmony_ci return clkdev->rate; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci return 0; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci/* Not supported by clk driver. Implemented to satisfy clk fw */ 25162306a36Sopenharmony_cistatic long skl_clk_round_rate(struct clk_hw *hw, unsigned long rate, 25262306a36Sopenharmony_ci unsigned long *parent_rate) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci return rate; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/* 25862306a36Sopenharmony_ci * prepare/unprepare are used instead of enable/disable as IPC will be sent 25962306a36Sopenharmony_ci * in non-atomic context. 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_cistatic const struct clk_ops skl_clk_ops = { 26262306a36Sopenharmony_ci .prepare = skl_clk_prepare, 26362306a36Sopenharmony_ci .unprepare = skl_clk_unprepare, 26462306a36Sopenharmony_ci .set_rate = skl_clk_set_rate, 26562306a36Sopenharmony_ci .round_rate = skl_clk_round_rate, 26662306a36Sopenharmony_ci .recalc_rate = skl_clk_recalc_rate, 26762306a36Sopenharmony_ci}; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic void unregister_parent_src_clk(struct skl_clk_parent *pclk, 27062306a36Sopenharmony_ci unsigned int id) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci while (id--) { 27362306a36Sopenharmony_ci clkdev_drop(pclk[id].lookup); 27462306a36Sopenharmony_ci clk_hw_unregister_fixed_rate(pclk[id].hw); 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic void unregister_src_clk(struct skl_clk_data *dclk) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci while (dclk->avail_clk_cnt--) 28162306a36Sopenharmony_ci clkdev_drop(dclk->clk[dclk->avail_clk_cnt]->lookup); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic int skl_register_parent_clks(struct device *dev, 28562306a36Sopenharmony_ci struct skl_clk_parent *parent, 28662306a36Sopenharmony_ci struct skl_clk_parent_src *pclk) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci int i, ret; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci for (i = 0; i < SKL_MAX_CLK_SRC; i++) { 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* Register Parent clock */ 29362306a36Sopenharmony_ci parent[i].hw = clk_hw_register_fixed_rate(dev, pclk[i].name, 29462306a36Sopenharmony_ci pclk[i].parent_name, 0, pclk[i].rate); 29562306a36Sopenharmony_ci if (IS_ERR(parent[i].hw)) { 29662306a36Sopenharmony_ci ret = PTR_ERR(parent[i].hw); 29762306a36Sopenharmony_ci goto err; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci parent[i].lookup = clkdev_hw_create(parent[i].hw, pclk[i].name, 30162306a36Sopenharmony_ci NULL); 30262306a36Sopenharmony_ci if (!parent[i].lookup) { 30362306a36Sopenharmony_ci clk_hw_unregister_fixed_rate(parent[i].hw); 30462306a36Sopenharmony_ci ret = -ENOMEM; 30562306a36Sopenharmony_ci goto err; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return 0; 31062306a36Sopenharmony_cierr: 31162306a36Sopenharmony_ci unregister_parent_src_clk(parent, i); 31262306a36Sopenharmony_ci return ret; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci/* Assign fmt_config to clk_data */ 31662306a36Sopenharmony_cistatic struct skl_clk *register_skl_clk(struct device *dev, 31762306a36Sopenharmony_ci struct skl_ssp_clk *clk, 31862306a36Sopenharmony_ci struct skl_clk_pdata *clk_pdata, int id) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct clk_init_data init; 32162306a36Sopenharmony_ci struct skl_clk *clkdev; 32262306a36Sopenharmony_ci int ret; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci clkdev = devm_kzalloc(dev, sizeof(*clkdev), GFP_KERNEL); 32562306a36Sopenharmony_ci if (!clkdev) 32662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci init.name = clk->name; 32962306a36Sopenharmony_ci init.ops = &skl_clk_ops; 33062306a36Sopenharmony_ci init.flags = CLK_SET_RATE_GATE; 33162306a36Sopenharmony_ci init.parent_names = &clk->parent_name; 33262306a36Sopenharmony_ci init.num_parents = 1; 33362306a36Sopenharmony_ci clkdev->hw.init = &init; 33462306a36Sopenharmony_ci clkdev->pdata = clk_pdata; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci clkdev->id = id; 33762306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, &clkdev->hw); 33862306a36Sopenharmony_ci if (ret) { 33962306a36Sopenharmony_ci clkdev = ERR_PTR(ret); 34062306a36Sopenharmony_ci return clkdev; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci clkdev->lookup = clkdev_hw_create(&clkdev->hw, init.name, NULL); 34462306a36Sopenharmony_ci if (!clkdev->lookup) 34562306a36Sopenharmony_ci clkdev = ERR_PTR(-ENOMEM); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return clkdev; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic int skl_clk_dev_probe(struct platform_device *pdev) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 35362306a36Sopenharmony_ci struct device *parent_dev = dev->parent; 35462306a36Sopenharmony_ci struct skl_clk_parent_src *parent_clks; 35562306a36Sopenharmony_ci struct skl_clk_pdata *clk_pdata; 35662306a36Sopenharmony_ci struct skl_clk_data *data; 35762306a36Sopenharmony_ci struct skl_ssp_clk *clks; 35862306a36Sopenharmony_ci int ret, i; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci clk_pdata = dev_get_platdata(&pdev->dev); 36162306a36Sopenharmony_ci parent_clks = clk_pdata->parent_clks; 36262306a36Sopenharmony_ci clks = clk_pdata->ssp_clks; 36362306a36Sopenharmony_ci if (!parent_clks || !clks) 36462306a36Sopenharmony_ci return -EIO; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 36762306a36Sopenharmony_ci if (!data) 36862306a36Sopenharmony_ci return -ENOMEM; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* Register Parent clock */ 37162306a36Sopenharmony_ci ret = skl_register_parent_clks(parent_dev, data->parent, parent_clks); 37262306a36Sopenharmony_ci if (ret < 0) 37362306a36Sopenharmony_ci return ret; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci for (i = 0; i < clk_pdata->num_clks; i++) { 37662306a36Sopenharmony_ci /* 37762306a36Sopenharmony_ci * Only register valid clocks 37862306a36Sopenharmony_ci * i.e. for which nhlt entry is present. 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_ci if (clks[i].rate_cfg[0].rate == 0) 38162306a36Sopenharmony_ci continue; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci data->clk[data->avail_clk_cnt] = register_skl_clk(dev, 38462306a36Sopenharmony_ci &clks[i], clk_pdata, i); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (IS_ERR(data->clk[data->avail_clk_cnt])) { 38762306a36Sopenharmony_ci ret = PTR_ERR(data->clk[data->avail_clk_cnt]); 38862306a36Sopenharmony_ci goto err_unreg_skl_clk; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci data->avail_clk_cnt++; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci platform_set_drvdata(pdev, data); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci return 0; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cierr_unreg_skl_clk: 39962306a36Sopenharmony_ci unregister_src_clk(data); 40062306a36Sopenharmony_ci unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return ret; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic void skl_clk_dev_remove(struct platform_device *pdev) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci struct skl_clk_data *data; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci data = platform_get_drvdata(pdev); 41062306a36Sopenharmony_ci unregister_src_clk(data); 41162306a36Sopenharmony_ci unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic struct platform_driver skl_clk_driver = { 41562306a36Sopenharmony_ci .driver = { 41662306a36Sopenharmony_ci .name = "skl-ssp-clk", 41762306a36Sopenharmony_ci }, 41862306a36Sopenharmony_ci .probe = skl_clk_dev_probe, 41962306a36Sopenharmony_ci .remove_new = skl_clk_dev_remove, 42062306a36Sopenharmony_ci}; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cimodule_platform_driver(skl_clk_driver); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ciMODULE_DESCRIPTION("Skylake clock driver"); 42562306a36Sopenharmony_ciMODULE_AUTHOR("Jaikrishna Nemallapudi <jaikrishnax.nemallapudi@intel.com>"); 42662306a36Sopenharmony_ciMODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>"); 42762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 42862306a36Sopenharmony_ciMODULE_ALIAS("platform:skl-ssp-clk"); 429