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