1e149ca29SPierre-Louis Bossart // SPDX-License-Identifier: GPL-2.0-only
201f50d69SSriram Periyasamy // Copyright(c) 2015-17 Intel Corporation
301f50d69SSriram Periyasamy 
401f50d69SSriram Periyasamy /*
501f50d69SSriram Periyasamy  *  skl-ssp-clk.c - ASoC skylake ssp clock driver
601f50d69SSriram Periyasamy  */
701f50d69SSriram Periyasamy 
801f50d69SSriram Periyasamy #include <linux/kernel.h>
901f50d69SSriram Periyasamy #include <linux/module.h>
1001f50d69SSriram Periyasamy #include <linux/err.h>
1101f50d69SSriram Periyasamy #include <linux/platform_device.h>
1201f50d69SSriram Periyasamy #include <linux/clk-provider.h>
1301f50d69SSriram Periyasamy #include <linux/clkdev.h>
1463643b59SPierre-Louis Bossart #include <sound/intel-nhlt.h>
1501f50d69SSriram Periyasamy #include "skl.h"
1601f50d69SSriram Periyasamy #include "skl-ssp-clk.h"
1701f50d69SSriram Periyasamy #include "skl-topology.h"
1801f50d69SSriram Periyasamy 
1901f50d69SSriram Periyasamy #define to_skl_clk(_hw)	container_of(_hw, struct skl_clk, hw)
2001f50d69SSriram Periyasamy 
2101f50d69SSriram Periyasamy struct skl_clk_parent {
2201f50d69SSriram Periyasamy 	struct clk_hw *hw;
2301f50d69SSriram Periyasamy 	struct clk_lookup *lookup;
2401f50d69SSriram Periyasamy };
2501f50d69SSriram Periyasamy 
2601f50d69SSriram Periyasamy struct skl_clk {
2701f50d69SSriram Periyasamy 	struct clk_hw hw;
2801f50d69SSriram Periyasamy 	struct clk_lookup *lookup;
2901f50d69SSriram Periyasamy 	unsigned long rate;
3001f50d69SSriram Periyasamy 	struct skl_clk_pdata *pdata;
3101f50d69SSriram Periyasamy 	u32 id;
3201f50d69SSriram Periyasamy };
3301f50d69SSriram Periyasamy 
3401f50d69SSriram Periyasamy struct skl_clk_data {
3501f50d69SSriram Periyasamy 	struct skl_clk_parent parent[SKL_MAX_CLK_SRC];
3601f50d69SSriram Periyasamy 	struct skl_clk *clk[SKL_MAX_CLK_CNT];
3701f50d69SSriram Periyasamy 	u8 avail_clk_cnt;
3801f50d69SSriram Periyasamy };
3901f50d69SSriram Periyasamy 
skl_get_clk_type(u32 index)4001f50d69SSriram Periyasamy static int skl_get_clk_type(u32 index)
4101f50d69SSriram Periyasamy {
4201f50d69SSriram Periyasamy 	switch (index) {
4301f50d69SSriram Periyasamy 	case 0 ... (SKL_SCLK_OFS - 1):
4401f50d69SSriram Periyasamy 		return SKL_MCLK;
4501f50d69SSriram Periyasamy 
4601f50d69SSriram Periyasamy 	case SKL_SCLK_OFS ... (SKL_SCLKFS_OFS - 1):
4701f50d69SSriram Periyasamy 		return SKL_SCLK;
4801f50d69SSriram Periyasamy 
4901f50d69SSriram Periyasamy 	case SKL_SCLKFS_OFS ... (SKL_MAX_CLK_CNT - 1):
5001f50d69SSriram Periyasamy 		return SKL_SCLK_FS;
5101f50d69SSriram Periyasamy 
5201f50d69SSriram Periyasamy 	default:
5301f50d69SSriram Periyasamy 		return -EINVAL;
5401f50d69SSriram Periyasamy 	}
5501f50d69SSriram Periyasamy }
5601f50d69SSriram Periyasamy 
skl_get_vbus_id(u32 index,u8 clk_type)5701f50d69SSriram Periyasamy static int skl_get_vbus_id(u32 index, u8 clk_type)
5801f50d69SSriram Periyasamy {
5901f50d69SSriram Periyasamy 	switch (clk_type) {
6001f50d69SSriram Periyasamy 	case SKL_MCLK:
6101f50d69SSriram Periyasamy 		return index;
6201f50d69SSriram Periyasamy 
6301f50d69SSriram Periyasamy 	case SKL_SCLK:
6401f50d69SSriram Periyasamy 		return index - SKL_SCLK_OFS;
6501f50d69SSriram Periyasamy 
6601f50d69SSriram Periyasamy 	case SKL_SCLK_FS:
6701f50d69SSriram Periyasamy 		return index - SKL_SCLKFS_OFS;
6801f50d69SSriram Periyasamy 
6901f50d69SSriram Periyasamy 	default:
7001f50d69SSriram Periyasamy 		return -EINVAL;
7101f50d69SSriram Periyasamy 	}
7201f50d69SSriram Periyasamy }
7301f50d69SSriram Periyasamy 
skl_fill_clk_ipc(struct skl_clk_rate_cfg_table * rcfg,u8 clk_type)7401f50d69SSriram Periyasamy static void skl_fill_clk_ipc(struct skl_clk_rate_cfg_table *rcfg, u8 clk_type)
7501f50d69SSriram Periyasamy {
7601f50d69SSriram Periyasamy 	struct nhlt_fmt_cfg *fmt_cfg;
7701f50d69SSriram Periyasamy 	union skl_clk_ctrl_ipc *ipc;
7801f50d69SSriram Periyasamy 	struct wav_fmt *wfmt;
7901f50d69SSriram Periyasamy 
8001f50d69SSriram Periyasamy 	if (!rcfg)
8101f50d69SSriram Periyasamy 		return;
8201f50d69SSriram Periyasamy 
8301f50d69SSriram Periyasamy 	ipc = &rcfg->dma_ctl_ipc;
8401f50d69SSriram Periyasamy 	if (clk_type == SKL_SCLK_FS) {
8501f50d69SSriram Periyasamy 		fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config;
8601f50d69SSriram Periyasamy 		wfmt = &fmt_cfg->fmt_ext.fmt;
8701f50d69SSriram Periyasamy 
8801f50d69SSriram Periyasamy 		/* Remove TLV Header size */
8901f50d69SSriram Periyasamy 		ipc->sclk_fs.hdr.size = sizeof(struct skl_dmactrl_sclkfs_cfg) -
9001f50d69SSriram Periyasamy 						sizeof(struct skl_tlv_hdr);
9101f50d69SSriram Periyasamy 		ipc->sclk_fs.sampling_frequency = wfmt->samples_per_sec;
9201f50d69SSriram Periyasamy 		ipc->sclk_fs.bit_depth = wfmt->bits_per_sample;
9301f50d69SSriram Periyasamy 		ipc->sclk_fs.valid_bit_depth =
9401f50d69SSriram Periyasamy 			fmt_cfg->fmt_ext.sample.valid_bits_per_sample;
9501f50d69SSriram Periyasamy 		ipc->sclk_fs.number_of_channels = wfmt->channels;
9601f50d69SSriram Periyasamy 	} else {
9701f50d69SSriram Periyasamy 		ipc->mclk.hdr.type = DMA_CLK_CONTROLS;
9801f50d69SSriram Periyasamy 		/* Remove TLV Header size */
9901f50d69SSriram Periyasamy 		ipc->mclk.hdr.size = sizeof(struct skl_dmactrl_mclk_cfg) -
10001f50d69SSriram Periyasamy 						sizeof(struct skl_tlv_hdr);
10101f50d69SSriram Periyasamy 	}
10201f50d69SSriram Periyasamy }
10301f50d69SSriram Periyasamy 
10401f50d69SSriram Periyasamy /* Sends dma control IPC to turn the clock ON/OFF */
skl_send_clk_dma_control(struct skl_dev * skl,struct skl_clk_rate_cfg_table * rcfg,u32 vbus_id,u8 clk_type,bool enable)105bcc2a2dcSCezary Rojewski static int skl_send_clk_dma_control(struct skl_dev *skl,
10601f50d69SSriram Periyasamy 				struct skl_clk_rate_cfg_table *rcfg,
10701f50d69SSriram Periyasamy 				u32 vbus_id, u8 clk_type,
10801f50d69SSriram Periyasamy 				bool enable)
10901f50d69SSriram Periyasamy {
11001f50d69SSriram Periyasamy 	struct nhlt_specific_cfg *sp_cfg;
11101f50d69SSriram Periyasamy 	u32 i2s_config_size, node_id = 0;
11201f50d69SSriram Periyasamy 	struct nhlt_fmt_cfg *fmt_cfg;
11301f50d69SSriram Periyasamy 	union skl_clk_ctrl_ipc *ipc;
11401f50d69SSriram Periyasamy 	void *i2s_config = NULL;
11501f50d69SSriram Periyasamy 	u8 *data, size;
11601f50d69SSriram Periyasamy 	int ret;
11701f50d69SSriram Periyasamy 
11801f50d69SSriram Periyasamy 	if (!rcfg)
11901f50d69SSriram Periyasamy 		return -EIO;
12001f50d69SSriram Periyasamy 
12101f50d69SSriram Periyasamy 	ipc = &rcfg->dma_ctl_ipc;
12201f50d69SSriram Periyasamy 	fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config;
12301f50d69SSriram Periyasamy 	sp_cfg = &fmt_cfg->config;
12401f50d69SSriram Periyasamy 
12501f50d69SSriram Periyasamy 	if (clk_type == SKL_SCLK_FS) {
12601f50d69SSriram Periyasamy 		ipc->sclk_fs.hdr.type =
12701f50d69SSriram Periyasamy 			enable ? DMA_TRANSMITION_START : DMA_TRANSMITION_STOP;
12801f50d69SSriram Periyasamy 		data = (u8 *)&ipc->sclk_fs;
12901f50d69SSriram Periyasamy 		size = sizeof(struct skl_dmactrl_sclkfs_cfg);
13001f50d69SSriram Periyasamy 	} else {
13101f50d69SSriram Periyasamy 		/* 1 to enable mclk, 0 to enable sclk */
13201f50d69SSriram Periyasamy 		if (clk_type == SKL_SCLK)
13301f50d69SSriram Periyasamy 			ipc->mclk.mclk = 0;
13401f50d69SSriram Periyasamy 		else
13501f50d69SSriram Periyasamy 			ipc->mclk.mclk = 1;
13601f50d69SSriram Periyasamy 
13701f50d69SSriram Periyasamy 		ipc->mclk.keep_running = enable;
13801f50d69SSriram Periyasamy 		ipc->mclk.warm_up_over = enable;
13901f50d69SSriram Periyasamy 		ipc->mclk.clk_stop_over = !enable;
14001f50d69SSriram Periyasamy 		data = (u8 *)&ipc->mclk;
14101f50d69SSriram Periyasamy 		size = sizeof(struct skl_dmactrl_mclk_cfg);
14201f50d69SSriram Periyasamy 	}
14301f50d69SSriram Periyasamy 
14401f50d69SSriram Periyasamy 	i2s_config_size = sp_cfg->size + size;
14501f50d69SSriram Periyasamy 	i2s_config = kzalloc(i2s_config_size, GFP_KERNEL);
14601f50d69SSriram Periyasamy 	if (!i2s_config)
14701f50d69SSriram Periyasamy 		return -ENOMEM;
14801f50d69SSriram Periyasamy 
14901f50d69SSriram Periyasamy 	/* copy blob */
15001f50d69SSriram Periyasamy 	memcpy(i2s_config, sp_cfg->caps, sp_cfg->size);
15101f50d69SSriram Periyasamy 
15201f50d69SSriram Periyasamy 	/* copy additional dma controls information */
15301f50d69SSriram Periyasamy 	memcpy(i2s_config + sp_cfg->size, data, size);
15401f50d69SSriram Periyasamy 
15501f50d69SSriram Periyasamy 	node_id = ((SKL_DMA_I2S_LINK_INPUT_CLASS << 8) | (vbus_id << 4));
156bcc2a2dcSCezary Rojewski 	ret = skl_dsp_set_dma_control(skl, (u32 *)i2s_config,
15701f50d69SSriram Periyasamy 					i2s_config_size, node_id);
15801f50d69SSriram Periyasamy 	kfree(i2s_config);
15901f50d69SSriram Periyasamy 
16001f50d69SSriram Periyasamy 	return ret;
16101f50d69SSriram Periyasamy }
16201f50d69SSriram Periyasamy 
skl_get_rate_cfg(struct skl_clk_rate_cfg_table * rcfg,unsigned long rate)16301f50d69SSriram Periyasamy static struct skl_clk_rate_cfg_table *skl_get_rate_cfg(
16401f50d69SSriram Periyasamy 		struct skl_clk_rate_cfg_table *rcfg,
16501f50d69SSriram Periyasamy 				unsigned long rate)
16601f50d69SSriram Periyasamy {
16701f50d69SSriram Periyasamy 	int i;
16801f50d69SSriram Periyasamy 
16901f50d69SSriram Periyasamy 	for (i = 0; (i < SKL_MAX_CLK_RATES) && rcfg[i].rate; i++) {
17001f50d69SSriram Periyasamy 		if (rcfg[i].rate == rate)
17101f50d69SSriram Periyasamy 			return &rcfg[i];
17201f50d69SSriram Periyasamy 	}
17301f50d69SSriram Periyasamy 
17401f50d69SSriram Periyasamy 	return NULL;
17501f50d69SSriram Periyasamy }
17601f50d69SSriram Periyasamy 
skl_clk_change_status(struct skl_clk * clkdev,bool enable)17701f50d69SSriram Periyasamy static int skl_clk_change_status(struct skl_clk *clkdev,
17801f50d69SSriram Periyasamy 				bool enable)
17901f50d69SSriram Periyasamy {
18001f50d69SSriram Periyasamy 	struct skl_clk_rate_cfg_table *rcfg;
18101f50d69SSriram Periyasamy 	int vbus_id, clk_type;
18201f50d69SSriram Periyasamy 
18301f50d69SSriram Periyasamy 	clk_type = skl_get_clk_type(clkdev->id);
18401f50d69SSriram Periyasamy 	if (clk_type < 0)
18501f50d69SSriram Periyasamy 		return clk_type;
18601f50d69SSriram Periyasamy 
18701f50d69SSriram Periyasamy 	vbus_id = skl_get_vbus_id(clkdev->id, clk_type);
18801f50d69SSriram Periyasamy 	if (vbus_id < 0)
18901f50d69SSriram Periyasamy 		return vbus_id;
19001f50d69SSriram Periyasamy 
19101f50d69SSriram Periyasamy 	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
19201f50d69SSriram Periyasamy 						clkdev->rate);
19301f50d69SSriram Periyasamy 	if (!rcfg)
19401f50d69SSriram Periyasamy 		return -EINVAL;
19501f50d69SSriram Periyasamy 
19601f50d69SSriram Periyasamy 	return skl_send_clk_dma_control(clkdev->pdata->pvt_data, rcfg,
19701f50d69SSriram Periyasamy 					vbus_id, clk_type, enable);
19801f50d69SSriram Periyasamy }
19901f50d69SSriram Periyasamy 
skl_clk_prepare(struct clk_hw * hw)20001f50d69SSriram Periyasamy static int skl_clk_prepare(struct clk_hw *hw)
20101f50d69SSriram Periyasamy {
20201f50d69SSriram Periyasamy 	struct skl_clk *clkdev = to_skl_clk(hw);
20301f50d69SSriram Periyasamy 
20401f50d69SSriram Periyasamy 	return skl_clk_change_status(clkdev, true);
20501f50d69SSriram Periyasamy }
20601f50d69SSriram Periyasamy 
skl_clk_unprepare(struct clk_hw * hw)20701f50d69SSriram Periyasamy static void skl_clk_unprepare(struct clk_hw *hw)
20801f50d69SSriram Periyasamy {
20901f50d69SSriram Periyasamy 	struct skl_clk *clkdev = to_skl_clk(hw);
21001f50d69SSriram Periyasamy 
21101f50d69SSriram Periyasamy 	skl_clk_change_status(clkdev, false);
21201f50d69SSriram Periyasamy }
21301f50d69SSriram Periyasamy 
skl_clk_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)21401f50d69SSriram Periyasamy static int skl_clk_set_rate(struct clk_hw *hw, unsigned long rate,
21501f50d69SSriram Periyasamy 					unsigned long parent_rate)
21601f50d69SSriram Periyasamy {
21701f50d69SSriram Periyasamy 	struct skl_clk *clkdev = to_skl_clk(hw);
21801f50d69SSriram Periyasamy 	struct skl_clk_rate_cfg_table *rcfg;
21901f50d69SSriram Periyasamy 	int clk_type;
22001f50d69SSriram Periyasamy 
22101f50d69SSriram Periyasamy 	if (!rate)
22201f50d69SSriram Periyasamy 		return -EINVAL;
22301f50d69SSriram Periyasamy 
22401f50d69SSriram Periyasamy 	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
22501f50d69SSriram Periyasamy 							rate);
22601f50d69SSriram Periyasamy 	if (!rcfg)
22701f50d69SSriram Periyasamy 		return -EINVAL;
22801f50d69SSriram Periyasamy 
22901f50d69SSriram Periyasamy 	clk_type = skl_get_clk_type(clkdev->id);
23001f50d69SSriram Periyasamy 	if (clk_type < 0)
23101f50d69SSriram Periyasamy 		return clk_type;
23201f50d69SSriram Periyasamy 
23301f50d69SSriram Periyasamy 	skl_fill_clk_ipc(rcfg, clk_type);
23401f50d69SSriram Periyasamy 	clkdev->rate = rate;
23501f50d69SSriram Periyasamy 
23601f50d69SSriram Periyasamy 	return 0;
23701f50d69SSriram Periyasamy }
23801f50d69SSriram Periyasamy 
skl_clk_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)23901f50d69SSriram Periyasamy static unsigned long skl_clk_recalc_rate(struct clk_hw *hw,
24001f50d69SSriram Periyasamy 				unsigned long parent_rate)
24101f50d69SSriram Periyasamy {
24201f50d69SSriram Periyasamy 	struct skl_clk *clkdev = to_skl_clk(hw);
24301f50d69SSriram Periyasamy 
24401f50d69SSriram Periyasamy 	if (clkdev->rate)
24501f50d69SSriram Periyasamy 		return clkdev->rate;
24601f50d69SSriram Periyasamy 
24701f50d69SSriram Periyasamy 	return 0;
24801f50d69SSriram Periyasamy }
24901f50d69SSriram Periyasamy 
25001f50d69SSriram Periyasamy /* Not supported by clk driver. Implemented to satisfy clk fw */
skl_clk_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * parent_rate)25129c5b8fdSColin Ian King static long skl_clk_round_rate(struct clk_hw *hw, unsigned long rate,
25201f50d69SSriram Periyasamy 			       unsigned long *parent_rate)
25301f50d69SSriram Periyasamy {
25401f50d69SSriram Periyasamy 	return rate;
25501f50d69SSriram Periyasamy }
25601f50d69SSriram Periyasamy 
25701f50d69SSriram Periyasamy /*
25801f50d69SSriram Periyasamy  * prepare/unprepare are used instead of enable/disable as IPC will be sent
25901f50d69SSriram Periyasamy  * in non-atomic context.
26001f50d69SSriram Periyasamy  */
26101f50d69SSriram Periyasamy static const struct clk_ops skl_clk_ops = {
26201f50d69SSriram Periyasamy 	.prepare = skl_clk_prepare,
26301f50d69SSriram Periyasamy 	.unprepare = skl_clk_unprepare,
26401f50d69SSriram Periyasamy 	.set_rate = skl_clk_set_rate,
26501f50d69SSriram Periyasamy 	.round_rate = skl_clk_round_rate,
26601f50d69SSriram Periyasamy 	.recalc_rate = skl_clk_recalc_rate,
26701f50d69SSriram Periyasamy };
26801f50d69SSriram Periyasamy 
unregister_parent_src_clk(struct skl_clk_parent * pclk,unsigned int id)26901f50d69SSriram Periyasamy static void unregister_parent_src_clk(struct skl_clk_parent *pclk,
27001f50d69SSriram Periyasamy 					unsigned int id)
27101f50d69SSriram Periyasamy {
27201f50d69SSriram Periyasamy 	while (id--) {
27301f50d69SSriram Periyasamy 		clkdev_drop(pclk[id].lookup);
27401f50d69SSriram Periyasamy 		clk_hw_unregister_fixed_rate(pclk[id].hw);
27501f50d69SSriram Periyasamy 	}
27601f50d69SSriram Periyasamy }
27701f50d69SSriram Periyasamy 
unregister_src_clk(struct skl_clk_data * dclk)27801f50d69SSriram Periyasamy static void unregister_src_clk(struct skl_clk_data *dclk)
27901f50d69SSriram Periyasamy {
2806ee927f2SAmadeusz Sławiński 	while (dclk->avail_clk_cnt--)
2816ee927f2SAmadeusz Sławiński 		clkdev_drop(dclk->clk[dclk->avail_clk_cnt]->lookup);
28201f50d69SSriram Periyasamy }
28301f50d69SSriram Periyasamy 
skl_register_parent_clks(struct device * dev,struct skl_clk_parent * parent,struct skl_clk_parent_src * pclk)28401f50d69SSriram Periyasamy static int skl_register_parent_clks(struct device *dev,
28501f50d69SSriram Periyasamy 			struct skl_clk_parent *parent,
28601f50d69SSriram Periyasamy 			struct skl_clk_parent_src *pclk)
28701f50d69SSriram Periyasamy {
28801f50d69SSriram Periyasamy 	int i, ret;
28901f50d69SSriram Periyasamy 
29001f50d69SSriram Periyasamy 	for (i = 0; i < SKL_MAX_CLK_SRC; i++) {
29101f50d69SSriram Periyasamy 
29201f50d69SSriram Periyasamy 		/* Register Parent clock */
29301f50d69SSriram Periyasamy 		parent[i].hw = clk_hw_register_fixed_rate(dev, pclk[i].name,
29401f50d69SSriram Periyasamy 				pclk[i].parent_name, 0, pclk[i].rate);
29501f50d69SSriram Periyasamy 		if (IS_ERR(parent[i].hw)) {
29601f50d69SSriram Periyasamy 			ret = PTR_ERR(parent[i].hw);
29701f50d69SSriram Periyasamy 			goto err;
29801f50d69SSriram Periyasamy 		}
29901f50d69SSriram Periyasamy 
30001f50d69SSriram Periyasamy 		parent[i].lookup = clkdev_hw_create(parent[i].hw, pclk[i].name,
30101f50d69SSriram Periyasamy 									NULL);
30201f50d69SSriram Periyasamy 		if (!parent[i].lookup) {
30301f50d69SSriram Periyasamy 			clk_hw_unregister_fixed_rate(parent[i].hw);
30401f50d69SSriram Periyasamy 			ret = -ENOMEM;
30501f50d69SSriram Periyasamy 			goto err;
30601f50d69SSriram Periyasamy 		}
30701f50d69SSriram Periyasamy 	}
30801f50d69SSriram Periyasamy 
30901f50d69SSriram Periyasamy 	return 0;
31001f50d69SSriram Periyasamy err:
31101f50d69SSriram Periyasamy 	unregister_parent_src_clk(parent, i);
31201f50d69SSriram Periyasamy 	return ret;
31301f50d69SSriram Periyasamy }
31401f50d69SSriram Periyasamy 
31501f50d69SSriram Periyasamy /* Assign fmt_config to clk_data */
register_skl_clk(struct device * dev,struct skl_ssp_clk * clk,struct skl_clk_pdata * clk_pdata,int id)31601f50d69SSriram Periyasamy static struct skl_clk *register_skl_clk(struct device *dev,
31701f50d69SSriram Periyasamy 			struct skl_ssp_clk *clk,
31801f50d69SSriram Periyasamy 			struct skl_clk_pdata *clk_pdata, int id)
31901f50d69SSriram Periyasamy {
32001f50d69SSriram Periyasamy 	struct clk_init_data init;
32101f50d69SSriram Periyasamy 	struct skl_clk *clkdev;
32201f50d69SSriram Periyasamy 	int ret;
32301f50d69SSriram Periyasamy 
32401f50d69SSriram Periyasamy 	clkdev = devm_kzalloc(dev, sizeof(*clkdev), GFP_KERNEL);
32501f50d69SSriram Periyasamy 	if (!clkdev)
32601f50d69SSriram Periyasamy 		return ERR_PTR(-ENOMEM);
32701f50d69SSriram Periyasamy 
32801f50d69SSriram Periyasamy 	init.name = clk->name;
32901f50d69SSriram Periyasamy 	init.ops = &skl_clk_ops;
33001f50d69SSriram Periyasamy 	init.flags = CLK_SET_RATE_GATE;
33101f50d69SSriram Periyasamy 	init.parent_names = &clk->parent_name;
33201f50d69SSriram Periyasamy 	init.num_parents = 1;
33301f50d69SSriram Periyasamy 	clkdev->hw.init = &init;
33401f50d69SSriram Periyasamy 	clkdev->pdata = clk_pdata;
33501f50d69SSriram Periyasamy 
33601f50d69SSriram Periyasamy 	clkdev->id = id;
33701f50d69SSriram Periyasamy 	ret = devm_clk_hw_register(dev, &clkdev->hw);
33801f50d69SSriram Periyasamy 	if (ret) {
33901f50d69SSriram Periyasamy 		clkdev = ERR_PTR(ret);
34001f50d69SSriram Periyasamy 		return clkdev;
34101f50d69SSriram Periyasamy 	}
34201f50d69SSriram Periyasamy 
34301f50d69SSriram Periyasamy 	clkdev->lookup = clkdev_hw_create(&clkdev->hw, init.name, NULL);
34401f50d69SSriram Periyasamy 	if (!clkdev->lookup)
34501f50d69SSriram Periyasamy 		clkdev = ERR_PTR(-ENOMEM);
34601f50d69SSriram Periyasamy 
34701f50d69SSriram Periyasamy 	return clkdev;
34801f50d69SSriram Periyasamy }
34901f50d69SSriram Periyasamy 
skl_clk_dev_probe(struct platform_device * pdev)35001f50d69SSriram Periyasamy static int skl_clk_dev_probe(struct platform_device *pdev)
35101f50d69SSriram Periyasamy {
35201f50d69SSriram Periyasamy 	struct device *dev = &pdev->dev;
35301f50d69SSriram Periyasamy 	struct device *parent_dev = dev->parent;
35401f50d69SSriram Periyasamy 	struct skl_clk_parent_src *parent_clks;
35501f50d69SSriram Periyasamy 	struct skl_clk_pdata *clk_pdata;
35601f50d69SSriram Periyasamy 	struct skl_clk_data *data;
35701f50d69SSriram Periyasamy 	struct skl_ssp_clk *clks;
35801f50d69SSriram Periyasamy 	int ret, i;
35901f50d69SSriram Periyasamy 
36001f50d69SSriram Periyasamy 	clk_pdata = dev_get_platdata(&pdev->dev);
36101f50d69SSriram Periyasamy 	parent_clks = clk_pdata->parent_clks;
36201f50d69SSriram Periyasamy 	clks = clk_pdata->ssp_clks;
36301f50d69SSriram Periyasamy 	if (!parent_clks || !clks)
36401f50d69SSriram Periyasamy 		return -EIO;
36501f50d69SSriram Periyasamy 
36601f50d69SSriram Periyasamy 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
36701f50d69SSriram Periyasamy 	if (!data)
36801f50d69SSriram Periyasamy 		return -ENOMEM;
36901f50d69SSriram Periyasamy 
37001f50d69SSriram Periyasamy 	/* Register Parent clock */
37101f50d69SSriram Periyasamy 	ret = skl_register_parent_clks(parent_dev, data->parent, parent_clks);
37201f50d69SSriram Periyasamy 	if (ret < 0)
37301f50d69SSriram Periyasamy 		return ret;
37401f50d69SSriram Periyasamy 
37501f50d69SSriram Periyasamy 	for (i = 0; i < clk_pdata->num_clks; i++) {
37601f50d69SSriram Periyasamy 		/*
37701f50d69SSriram Periyasamy 		 * Only register valid clocks
37801f50d69SSriram Periyasamy 		 * i.e. for which nhlt entry is present.
37901f50d69SSriram Periyasamy 		 */
38001f50d69SSriram Periyasamy 		if (clks[i].rate_cfg[0].rate == 0)
38101f50d69SSriram Periyasamy 			continue;
38201f50d69SSriram Periyasamy 
3836ee927f2SAmadeusz Sławiński 		data->clk[data->avail_clk_cnt] = register_skl_clk(dev,
3846ee927f2SAmadeusz Sławiński 				&clks[i], clk_pdata, i);
3856ee927f2SAmadeusz Sławiński 
3866ee927f2SAmadeusz Sławiński 		if (IS_ERR(data->clk[data->avail_clk_cnt])) {
3878308a09eSAmadeusz Sławiński 			ret = PTR_ERR(data->clk[data->avail_clk_cnt]);
38801f50d69SSriram Periyasamy 			goto err_unreg_skl_clk;
38901f50d69SSriram Periyasamy 		}
3908308a09eSAmadeusz Sławiński 
3918308a09eSAmadeusz Sławiński 		data->avail_clk_cnt++;
39201f50d69SSriram Periyasamy 	}
39301f50d69SSriram Periyasamy 
39401f50d69SSriram Periyasamy 	platform_set_drvdata(pdev, data);
39501f50d69SSriram Periyasamy 
39601f50d69SSriram Periyasamy 	return 0;
39701f50d69SSriram Periyasamy 
39801f50d69SSriram Periyasamy err_unreg_skl_clk:
39901f50d69SSriram Periyasamy 	unregister_src_clk(data);
40001f50d69SSriram Periyasamy 	unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
40101f50d69SSriram Periyasamy 
40201f50d69SSriram Periyasamy 	return ret;
40301f50d69SSriram Periyasamy }
40401f50d69SSriram Periyasamy 
skl_clk_dev_remove(struct platform_device * pdev)405*394b2a11SUwe Kleine-König static void skl_clk_dev_remove(struct platform_device *pdev)
40601f50d69SSriram Periyasamy {
40701f50d69SSriram Periyasamy 	struct skl_clk_data *data;
40801f50d69SSriram Periyasamy 
40901f50d69SSriram Periyasamy 	data = platform_get_drvdata(pdev);
41001f50d69SSriram Periyasamy 	unregister_src_clk(data);
41101f50d69SSriram Periyasamy 	unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
41201f50d69SSriram Periyasamy }
41301f50d69SSriram Periyasamy 
41401f50d69SSriram Periyasamy static struct platform_driver skl_clk_driver = {
41501f50d69SSriram Periyasamy 	.driver = {
41601f50d69SSriram Periyasamy 		.name = "skl-ssp-clk",
41701f50d69SSriram Periyasamy 	},
41801f50d69SSriram Periyasamy 	.probe = skl_clk_dev_probe,
419*394b2a11SUwe Kleine-König 	.remove_new = skl_clk_dev_remove,
42001f50d69SSriram Periyasamy };
42101f50d69SSriram Periyasamy 
42201f50d69SSriram Periyasamy module_platform_driver(skl_clk_driver);
42301f50d69SSriram Periyasamy 
42401f50d69SSriram Periyasamy MODULE_DESCRIPTION("Skylake clock driver");
42501f50d69SSriram Periyasamy MODULE_AUTHOR("Jaikrishna Nemallapudi <jaikrishnax.nemallapudi@intel.com>");
42601f50d69SSriram Periyasamy MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
42701f50d69SSriram Periyasamy MODULE_LICENSE("GPL v2");
42801f50d69SSriram Periyasamy MODULE_ALIAS("platform:skl-ssp-clk");
429