18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci// Copyright(c) 2015-17 Intel Corporation
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci/*
58c2ecf20Sopenharmony_ci *  skl-ssp-clk.c - ASoC skylake ssp clock driver
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/err.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
138c2ecf20Sopenharmony_ci#include <linux/clkdev.h>
148c2ecf20Sopenharmony_ci#include <sound/intel-nhlt.h>
158c2ecf20Sopenharmony_ci#include "skl.h"
168c2ecf20Sopenharmony_ci#include "skl-ssp-clk.h"
178c2ecf20Sopenharmony_ci#include "skl-topology.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define to_skl_clk(_hw)	container_of(_hw, struct skl_clk, hw)
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistruct skl_clk_parent {
228c2ecf20Sopenharmony_ci	struct clk_hw *hw;
238c2ecf20Sopenharmony_ci	struct clk_lookup *lookup;
248c2ecf20Sopenharmony_ci};
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistruct skl_clk {
278c2ecf20Sopenharmony_ci	struct clk_hw hw;
288c2ecf20Sopenharmony_ci	struct clk_lookup *lookup;
298c2ecf20Sopenharmony_ci	unsigned long rate;
308c2ecf20Sopenharmony_ci	struct skl_clk_pdata *pdata;
318c2ecf20Sopenharmony_ci	u32 id;
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistruct skl_clk_data {
358c2ecf20Sopenharmony_ci	struct skl_clk_parent parent[SKL_MAX_CLK_SRC];
368c2ecf20Sopenharmony_ci	struct skl_clk *clk[SKL_MAX_CLK_CNT];
378c2ecf20Sopenharmony_ci	u8 avail_clk_cnt;
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int skl_get_clk_type(u32 index)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	switch (index) {
438c2ecf20Sopenharmony_ci	case 0 ... (SKL_SCLK_OFS - 1):
448c2ecf20Sopenharmony_ci		return SKL_MCLK;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	case SKL_SCLK_OFS ... (SKL_SCLKFS_OFS - 1):
478c2ecf20Sopenharmony_ci		return SKL_SCLK;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	case SKL_SCLKFS_OFS ... (SKL_MAX_CLK_CNT - 1):
508c2ecf20Sopenharmony_ci		return SKL_SCLK_FS;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	default:
538c2ecf20Sopenharmony_ci		return -EINVAL;
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic int skl_get_vbus_id(u32 index, u8 clk_type)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	switch (clk_type) {
608c2ecf20Sopenharmony_ci	case SKL_MCLK:
618c2ecf20Sopenharmony_ci		return index;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	case SKL_SCLK:
648c2ecf20Sopenharmony_ci		return index - SKL_SCLK_OFS;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	case SKL_SCLK_FS:
678c2ecf20Sopenharmony_ci		return index - SKL_SCLKFS_OFS;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	default:
708c2ecf20Sopenharmony_ci		return -EINVAL;
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic void skl_fill_clk_ipc(struct skl_clk_rate_cfg_table *rcfg, u8 clk_type)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	struct nhlt_fmt_cfg *fmt_cfg;
778c2ecf20Sopenharmony_ci	union skl_clk_ctrl_ipc *ipc;
788c2ecf20Sopenharmony_ci	struct wav_fmt *wfmt;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (!rcfg)
818c2ecf20Sopenharmony_ci		return;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	ipc = &rcfg->dma_ctl_ipc;
848c2ecf20Sopenharmony_ci	if (clk_type == SKL_SCLK_FS) {
858c2ecf20Sopenharmony_ci		fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config;
868c2ecf20Sopenharmony_ci		wfmt = &fmt_cfg->fmt_ext.fmt;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci		/* Remove TLV Header size */
898c2ecf20Sopenharmony_ci		ipc->sclk_fs.hdr.size = sizeof(struct skl_dmactrl_sclkfs_cfg) -
908c2ecf20Sopenharmony_ci						sizeof(struct skl_tlv_hdr);
918c2ecf20Sopenharmony_ci		ipc->sclk_fs.sampling_frequency = wfmt->samples_per_sec;
928c2ecf20Sopenharmony_ci		ipc->sclk_fs.bit_depth = wfmt->bits_per_sample;
938c2ecf20Sopenharmony_ci		ipc->sclk_fs.valid_bit_depth =
948c2ecf20Sopenharmony_ci			fmt_cfg->fmt_ext.sample.valid_bits_per_sample;
958c2ecf20Sopenharmony_ci		ipc->sclk_fs.number_of_channels = wfmt->channels;
968c2ecf20Sopenharmony_ci	} else {
978c2ecf20Sopenharmony_ci		ipc->mclk.hdr.type = DMA_CLK_CONTROLS;
988c2ecf20Sopenharmony_ci		/* Remove TLV Header size */
998c2ecf20Sopenharmony_ci		ipc->mclk.hdr.size = sizeof(struct skl_dmactrl_mclk_cfg) -
1008c2ecf20Sopenharmony_ci						sizeof(struct skl_tlv_hdr);
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/* Sends dma control IPC to turn the clock ON/OFF */
1058c2ecf20Sopenharmony_cistatic int skl_send_clk_dma_control(struct skl_dev *skl,
1068c2ecf20Sopenharmony_ci				struct skl_clk_rate_cfg_table *rcfg,
1078c2ecf20Sopenharmony_ci				u32 vbus_id, u8 clk_type,
1088c2ecf20Sopenharmony_ci				bool enable)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct nhlt_specific_cfg *sp_cfg;
1118c2ecf20Sopenharmony_ci	u32 i2s_config_size, node_id = 0;
1128c2ecf20Sopenharmony_ci	struct nhlt_fmt_cfg *fmt_cfg;
1138c2ecf20Sopenharmony_ci	union skl_clk_ctrl_ipc *ipc;
1148c2ecf20Sopenharmony_ci	void *i2s_config = NULL;
1158c2ecf20Sopenharmony_ci	u8 *data, size;
1168c2ecf20Sopenharmony_ci	int ret;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (!rcfg)
1198c2ecf20Sopenharmony_ci		return -EIO;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	ipc = &rcfg->dma_ctl_ipc;
1228c2ecf20Sopenharmony_ci	fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config;
1238c2ecf20Sopenharmony_ci	sp_cfg = &fmt_cfg->config;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (clk_type == SKL_SCLK_FS) {
1268c2ecf20Sopenharmony_ci		ipc->sclk_fs.hdr.type =
1278c2ecf20Sopenharmony_ci			enable ? DMA_TRANSMITION_START : DMA_TRANSMITION_STOP;
1288c2ecf20Sopenharmony_ci		data = (u8 *)&ipc->sclk_fs;
1298c2ecf20Sopenharmony_ci		size = sizeof(struct skl_dmactrl_sclkfs_cfg);
1308c2ecf20Sopenharmony_ci	} else {
1318c2ecf20Sopenharmony_ci		/* 1 to enable mclk, 0 to enable sclk */
1328c2ecf20Sopenharmony_ci		if (clk_type == SKL_SCLK)
1338c2ecf20Sopenharmony_ci			ipc->mclk.mclk = 0;
1348c2ecf20Sopenharmony_ci		else
1358c2ecf20Sopenharmony_ci			ipc->mclk.mclk = 1;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci		ipc->mclk.keep_running = enable;
1388c2ecf20Sopenharmony_ci		ipc->mclk.warm_up_over = enable;
1398c2ecf20Sopenharmony_ci		ipc->mclk.clk_stop_over = !enable;
1408c2ecf20Sopenharmony_ci		data = (u8 *)&ipc->mclk;
1418c2ecf20Sopenharmony_ci		size = sizeof(struct skl_dmactrl_mclk_cfg);
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	i2s_config_size = sp_cfg->size + size;
1458c2ecf20Sopenharmony_ci	i2s_config = kzalloc(i2s_config_size, GFP_KERNEL);
1468c2ecf20Sopenharmony_ci	if (!i2s_config)
1478c2ecf20Sopenharmony_ci		return -ENOMEM;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/* copy blob */
1508c2ecf20Sopenharmony_ci	memcpy(i2s_config, sp_cfg->caps, sp_cfg->size);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	/* copy additional dma controls information */
1538c2ecf20Sopenharmony_ci	memcpy(i2s_config + sp_cfg->size, data, size);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	node_id = ((SKL_DMA_I2S_LINK_INPUT_CLASS << 8) | (vbus_id << 4));
1568c2ecf20Sopenharmony_ci	ret = skl_dsp_set_dma_control(skl, (u32 *)i2s_config,
1578c2ecf20Sopenharmony_ci					i2s_config_size, node_id);
1588c2ecf20Sopenharmony_ci	kfree(i2s_config);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	return ret;
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic struct skl_clk_rate_cfg_table *skl_get_rate_cfg(
1648c2ecf20Sopenharmony_ci		struct skl_clk_rate_cfg_table *rcfg,
1658c2ecf20Sopenharmony_ci				unsigned long rate)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	int i;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	for (i = 0; (i < SKL_MAX_CLK_RATES) && rcfg[i].rate; i++) {
1708c2ecf20Sopenharmony_ci		if (rcfg[i].rate == rate)
1718c2ecf20Sopenharmony_ci			return &rcfg[i];
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	return NULL;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic int skl_clk_change_status(struct skl_clk *clkdev,
1788c2ecf20Sopenharmony_ci				bool enable)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	struct skl_clk_rate_cfg_table *rcfg;
1818c2ecf20Sopenharmony_ci	int vbus_id, clk_type;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	clk_type = skl_get_clk_type(clkdev->id);
1848c2ecf20Sopenharmony_ci	if (clk_type < 0)
1858c2ecf20Sopenharmony_ci		return clk_type;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	vbus_id = skl_get_vbus_id(clkdev->id, clk_type);
1888c2ecf20Sopenharmony_ci	if (vbus_id < 0)
1898c2ecf20Sopenharmony_ci		return vbus_id;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
1928c2ecf20Sopenharmony_ci						clkdev->rate);
1938c2ecf20Sopenharmony_ci	if (!rcfg)
1948c2ecf20Sopenharmony_ci		return -EINVAL;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	return skl_send_clk_dma_control(clkdev->pdata->pvt_data, rcfg,
1978c2ecf20Sopenharmony_ci					vbus_id, clk_type, enable);
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic int skl_clk_prepare(struct clk_hw *hw)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	struct skl_clk *clkdev = to_skl_clk(hw);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	return skl_clk_change_status(clkdev, true);
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic void skl_clk_unprepare(struct clk_hw *hw)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	struct skl_clk *clkdev = to_skl_clk(hw);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	skl_clk_change_status(clkdev, false);
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic int skl_clk_set_rate(struct clk_hw *hw, unsigned long rate,
2158c2ecf20Sopenharmony_ci					unsigned long parent_rate)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	struct skl_clk *clkdev = to_skl_clk(hw);
2188c2ecf20Sopenharmony_ci	struct skl_clk_rate_cfg_table *rcfg;
2198c2ecf20Sopenharmony_ci	int clk_type;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	if (!rate)
2228c2ecf20Sopenharmony_ci		return -EINVAL;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
2258c2ecf20Sopenharmony_ci							rate);
2268c2ecf20Sopenharmony_ci	if (!rcfg)
2278c2ecf20Sopenharmony_ci		return -EINVAL;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	clk_type = skl_get_clk_type(clkdev->id);
2308c2ecf20Sopenharmony_ci	if (clk_type < 0)
2318c2ecf20Sopenharmony_ci		return clk_type;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	skl_fill_clk_ipc(rcfg, clk_type);
2348c2ecf20Sopenharmony_ci	clkdev->rate = rate;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	return 0;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic unsigned long skl_clk_recalc_rate(struct clk_hw *hw,
2408c2ecf20Sopenharmony_ci				unsigned long parent_rate)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	struct skl_clk *clkdev = to_skl_clk(hw);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (clkdev->rate)
2458c2ecf20Sopenharmony_ci		return clkdev->rate;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	return 0;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci/* Not supported by clk driver. Implemented to satisfy clk fw */
2518c2ecf20Sopenharmony_cistatic long skl_clk_round_rate(struct clk_hw *hw, unsigned long rate,
2528c2ecf20Sopenharmony_ci			       unsigned long *parent_rate)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	return rate;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci/*
2588c2ecf20Sopenharmony_ci * prepare/unprepare are used instead of enable/disable as IPC will be sent
2598c2ecf20Sopenharmony_ci * in non-atomic context.
2608c2ecf20Sopenharmony_ci */
2618c2ecf20Sopenharmony_cistatic const struct clk_ops skl_clk_ops = {
2628c2ecf20Sopenharmony_ci	.prepare = skl_clk_prepare,
2638c2ecf20Sopenharmony_ci	.unprepare = skl_clk_unprepare,
2648c2ecf20Sopenharmony_ci	.set_rate = skl_clk_set_rate,
2658c2ecf20Sopenharmony_ci	.round_rate = skl_clk_round_rate,
2668c2ecf20Sopenharmony_ci	.recalc_rate = skl_clk_recalc_rate,
2678c2ecf20Sopenharmony_ci};
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic void unregister_parent_src_clk(struct skl_clk_parent *pclk,
2708c2ecf20Sopenharmony_ci					unsigned int id)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	while (id--) {
2738c2ecf20Sopenharmony_ci		clkdev_drop(pclk[id].lookup);
2748c2ecf20Sopenharmony_ci		clk_hw_unregister_fixed_rate(pclk[id].hw);
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic void unregister_src_clk(struct skl_clk_data *dclk)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	while (dclk->avail_clk_cnt--)
2818c2ecf20Sopenharmony_ci		clkdev_drop(dclk->clk[dclk->avail_clk_cnt]->lookup);
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic int skl_register_parent_clks(struct device *dev,
2858c2ecf20Sopenharmony_ci			struct skl_clk_parent *parent,
2868c2ecf20Sopenharmony_ci			struct skl_clk_parent_src *pclk)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	int i, ret;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	for (i = 0; i < SKL_MAX_CLK_SRC; i++) {
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci		/* Register Parent clock */
2938c2ecf20Sopenharmony_ci		parent[i].hw = clk_hw_register_fixed_rate(dev, pclk[i].name,
2948c2ecf20Sopenharmony_ci				pclk[i].parent_name, 0, pclk[i].rate);
2958c2ecf20Sopenharmony_ci		if (IS_ERR(parent[i].hw)) {
2968c2ecf20Sopenharmony_ci			ret = PTR_ERR(parent[i].hw);
2978c2ecf20Sopenharmony_ci			goto err;
2988c2ecf20Sopenharmony_ci		}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci		parent[i].lookup = clkdev_hw_create(parent[i].hw, pclk[i].name,
3018c2ecf20Sopenharmony_ci									NULL);
3028c2ecf20Sopenharmony_ci		if (!parent[i].lookup) {
3038c2ecf20Sopenharmony_ci			clk_hw_unregister_fixed_rate(parent[i].hw);
3048c2ecf20Sopenharmony_ci			ret = -ENOMEM;
3058c2ecf20Sopenharmony_ci			goto err;
3068c2ecf20Sopenharmony_ci		}
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	return 0;
3108c2ecf20Sopenharmony_cierr:
3118c2ecf20Sopenharmony_ci	unregister_parent_src_clk(parent, i);
3128c2ecf20Sopenharmony_ci	return ret;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci/* Assign fmt_config to clk_data */
3168c2ecf20Sopenharmony_cistatic struct skl_clk *register_skl_clk(struct device *dev,
3178c2ecf20Sopenharmony_ci			struct skl_ssp_clk *clk,
3188c2ecf20Sopenharmony_ci			struct skl_clk_pdata *clk_pdata, int id)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	struct clk_init_data init;
3218c2ecf20Sopenharmony_ci	struct skl_clk *clkdev;
3228c2ecf20Sopenharmony_ci	int ret;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	clkdev = devm_kzalloc(dev, sizeof(*clkdev), GFP_KERNEL);
3258c2ecf20Sopenharmony_ci	if (!clkdev)
3268c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	init.name = clk->name;
3298c2ecf20Sopenharmony_ci	init.ops = &skl_clk_ops;
3308c2ecf20Sopenharmony_ci	init.flags = CLK_SET_RATE_GATE;
3318c2ecf20Sopenharmony_ci	init.parent_names = &clk->parent_name;
3328c2ecf20Sopenharmony_ci	init.num_parents = 1;
3338c2ecf20Sopenharmony_ci	clkdev->hw.init = &init;
3348c2ecf20Sopenharmony_ci	clkdev->pdata = clk_pdata;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	clkdev->id = id;
3378c2ecf20Sopenharmony_ci	ret = devm_clk_hw_register(dev, &clkdev->hw);
3388c2ecf20Sopenharmony_ci	if (ret) {
3398c2ecf20Sopenharmony_ci		clkdev = ERR_PTR(ret);
3408c2ecf20Sopenharmony_ci		return clkdev;
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	clkdev->lookup = clkdev_hw_create(&clkdev->hw, init.name, NULL);
3448c2ecf20Sopenharmony_ci	if (!clkdev->lookup)
3458c2ecf20Sopenharmony_ci		clkdev = ERR_PTR(-ENOMEM);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	return clkdev;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic int skl_clk_dev_probe(struct platform_device *pdev)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
3538c2ecf20Sopenharmony_ci	struct device *parent_dev = dev->parent;
3548c2ecf20Sopenharmony_ci	struct skl_clk_parent_src *parent_clks;
3558c2ecf20Sopenharmony_ci	struct skl_clk_pdata *clk_pdata;
3568c2ecf20Sopenharmony_ci	struct skl_clk_data *data;
3578c2ecf20Sopenharmony_ci	struct skl_ssp_clk *clks;
3588c2ecf20Sopenharmony_ci	int ret, i;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	clk_pdata = dev_get_platdata(&pdev->dev);
3618c2ecf20Sopenharmony_ci	parent_clks = clk_pdata->parent_clks;
3628c2ecf20Sopenharmony_ci	clks = clk_pdata->ssp_clks;
3638c2ecf20Sopenharmony_ci	if (!parent_clks || !clks)
3648c2ecf20Sopenharmony_ci		return -EIO;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
3678c2ecf20Sopenharmony_ci	if (!data)
3688c2ecf20Sopenharmony_ci		return -ENOMEM;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	/* Register Parent clock */
3718c2ecf20Sopenharmony_ci	ret = skl_register_parent_clks(parent_dev, data->parent, parent_clks);
3728c2ecf20Sopenharmony_ci	if (ret < 0)
3738c2ecf20Sopenharmony_ci		return ret;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	for (i = 0; i < clk_pdata->num_clks; i++) {
3768c2ecf20Sopenharmony_ci		/*
3778c2ecf20Sopenharmony_ci		 * Only register valid clocks
3788c2ecf20Sopenharmony_ci		 * i.e. for which nhlt entry is present.
3798c2ecf20Sopenharmony_ci		 */
3808c2ecf20Sopenharmony_ci		if (clks[i].rate_cfg[0].rate == 0)
3818c2ecf20Sopenharmony_ci			continue;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci		data->clk[data->avail_clk_cnt] = register_skl_clk(dev,
3848c2ecf20Sopenharmony_ci				&clks[i], clk_pdata, i);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci		if (IS_ERR(data->clk[data->avail_clk_cnt])) {
3878c2ecf20Sopenharmony_ci			ret = PTR_ERR(data->clk[data->avail_clk_cnt]);
3888c2ecf20Sopenharmony_ci			goto err_unreg_skl_clk;
3898c2ecf20Sopenharmony_ci		}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci		data->avail_clk_cnt++;
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, data);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	return 0;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cierr_unreg_skl_clk:
3998c2ecf20Sopenharmony_ci	unregister_src_clk(data);
4008c2ecf20Sopenharmony_ci	unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	return ret;
4038c2ecf20Sopenharmony_ci}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cistatic int skl_clk_dev_remove(struct platform_device *pdev)
4068c2ecf20Sopenharmony_ci{
4078c2ecf20Sopenharmony_ci	struct skl_clk_data *data;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	data = platform_get_drvdata(pdev);
4108c2ecf20Sopenharmony_ci	unregister_src_clk(data);
4118c2ecf20Sopenharmony_ci	unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	return 0;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic struct platform_driver skl_clk_driver = {
4178c2ecf20Sopenharmony_ci	.driver = {
4188c2ecf20Sopenharmony_ci		.name = "skl-ssp-clk",
4198c2ecf20Sopenharmony_ci	},
4208c2ecf20Sopenharmony_ci	.probe = skl_clk_dev_probe,
4218c2ecf20Sopenharmony_ci	.remove = skl_clk_dev_remove,
4228c2ecf20Sopenharmony_ci};
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cimodule_platform_driver(skl_clk_driver);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Skylake clock driver");
4278c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaikrishna Nemallapudi <jaikrishnax.nemallapudi@intel.com>");
4288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
4298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
4308c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:skl-ssp-clk");
431