cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

skl-ssp-clk.c (9729B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2// Copyright(c) 2015-17 Intel Corporation
      3
      4/*
      5 *  skl-ssp-clk.c - ASoC skylake ssp clock driver
      6 */
      7
      8#include <linux/kernel.h>
      9#include <linux/module.h>
     10#include <linux/err.h>
     11#include <linux/platform_device.h>
     12#include <linux/clk-provider.h>
     13#include <linux/clkdev.h>
     14#include <sound/intel-nhlt.h>
     15#include "skl.h"
     16#include "skl-ssp-clk.h"
     17#include "skl-topology.h"
     18
     19#define to_skl_clk(_hw)	container_of(_hw, struct skl_clk, hw)
     20
     21struct skl_clk_parent {
     22	struct clk_hw *hw;
     23	struct clk_lookup *lookup;
     24};
     25
     26struct skl_clk {
     27	struct clk_hw hw;
     28	struct clk_lookup *lookup;
     29	unsigned long rate;
     30	struct skl_clk_pdata *pdata;
     31	u32 id;
     32};
     33
     34struct skl_clk_data {
     35	struct skl_clk_parent parent[SKL_MAX_CLK_SRC];
     36	struct skl_clk *clk[SKL_MAX_CLK_CNT];
     37	u8 avail_clk_cnt;
     38};
     39
     40static int skl_get_clk_type(u32 index)
     41{
     42	switch (index) {
     43	case 0 ... (SKL_SCLK_OFS - 1):
     44		return SKL_MCLK;
     45
     46	case SKL_SCLK_OFS ... (SKL_SCLKFS_OFS - 1):
     47		return SKL_SCLK;
     48
     49	case SKL_SCLKFS_OFS ... (SKL_MAX_CLK_CNT - 1):
     50		return SKL_SCLK_FS;
     51
     52	default:
     53		return -EINVAL;
     54	}
     55}
     56
     57static int skl_get_vbus_id(u32 index, u8 clk_type)
     58{
     59	switch (clk_type) {
     60	case SKL_MCLK:
     61		return index;
     62
     63	case SKL_SCLK:
     64		return index - SKL_SCLK_OFS;
     65
     66	case SKL_SCLK_FS:
     67		return index - SKL_SCLKFS_OFS;
     68
     69	default:
     70		return -EINVAL;
     71	}
     72}
     73
     74static void skl_fill_clk_ipc(struct skl_clk_rate_cfg_table *rcfg, u8 clk_type)
     75{
     76	struct nhlt_fmt_cfg *fmt_cfg;
     77	union skl_clk_ctrl_ipc *ipc;
     78	struct wav_fmt *wfmt;
     79
     80	if (!rcfg)
     81		return;
     82
     83	ipc = &rcfg->dma_ctl_ipc;
     84	if (clk_type == SKL_SCLK_FS) {
     85		fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config;
     86		wfmt = &fmt_cfg->fmt_ext.fmt;
     87
     88		/* Remove TLV Header size */
     89		ipc->sclk_fs.hdr.size = sizeof(struct skl_dmactrl_sclkfs_cfg) -
     90						sizeof(struct skl_tlv_hdr);
     91		ipc->sclk_fs.sampling_frequency = wfmt->samples_per_sec;
     92		ipc->sclk_fs.bit_depth = wfmt->bits_per_sample;
     93		ipc->sclk_fs.valid_bit_depth =
     94			fmt_cfg->fmt_ext.sample.valid_bits_per_sample;
     95		ipc->sclk_fs.number_of_channels = wfmt->channels;
     96	} else {
     97		ipc->mclk.hdr.type = DMA_CLK_CONTROLS;
     98		/* Remove TLV Header size */
     99		ipc->mclk.hdr.size = sizeof(struct skl_dmactrl_mclk_cfg) -
    100						sizeof(struct skl_tlv_hdr);
    101	}
    102}
    103
    104/* Sends dma control IPC to turn the clock ON/OFF */
    105static int skl_send_clk_dma_control(struct skl_dev *skl,
    106				struct skl_clk_rate_cfg_table *rcfg,
    107				u32 vbus_id, u8 clk_type,
    108				bool enable)
    109{
    110	struct nhlt_specific_cfg *sp_cfg;
    111	u32 i2s_config_size, node_id = 0;
    112	struct nhlt_fmt_cfg *fmt_cfg;
    113	union skl_clk_ctrl_ipc *ipc;
    114	void *i2s_config = NULL;
    115	u8 *data, size;
    116	int ret;
    117
    118	if (!rcfg)
    119		return -EIO;
    120
    121	ipc = &rcfg->dma_ctl_ipc;
    122	fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config;
    123	sp_cfg = &fmt_cfg->config;
    124
    125	if (clk_type == SKL_SCLK_FS) {
    126		ipc->sclk_fs.hdr.type =
    127			enable ? DMA_TRANSMITION_START : DMA_TRANSMITION_STOP;
    128		data = (u8 *)&ipc->sclk_fs;
    129		size = sizeof(struct skl_dmactrl_sclkfs_cfg);
    130	} else {
    131		/* 1 to enable mclk, 0 to enable sclk */
    132		if (clk_type == SKL_SCLK)
    133			ipc->mclk.mclk = 0;
    134		else
    135			ipc->mclk.mclk = 1;
    136
    137		ipc->mclk.keep_running = enable;
    138		ipc->mclk.warm_up_over = enable;
    139		ipc->mclk.clk_stop_over = !enable;
    140		data = (u8 *)&ipc->mclk;
    141		size = sizeof(struct skl_dmactrl_mclk_cfg);
    142	}
    143
    144	i2s_config_size = sp_cfg->size + size;
    145	i2s_config = kzalloc(i2s_config_size, GFP_KERNEL);
    146	if (!i2s_config)
    147		return -ENOMEM;
    148
    149	/* copy blob */
    150	memcpy(i2s_config, sp_cfg->caps, sp_cfg->size);
    151
    152	/* copy additional dma controls information */
    153	memcpy(i2s_config + sp_cfg->size, data, size);
    154
    155	node_id = ((SKL_DMA_I2S_LINK_INPUT_CLASS << 8) | (vbus_id << 4));
    156	ret = skl_dsp_set_dma_control(skl, (u32 *)i2s_config,
    157					i2s_config_size, node_id);
    158	kfree(i2s_config);
    159
    160	return ret;
    161}
    162
    163static struct skl_clk_rate_cfg_table *skl_get_rate_cfg(
    164		struct skl_clk_rate_cfg_table *rcfg,
    165				unsigned long rate)
    166{
    167	int i;
    168
    169	for (i = 0; (i < SKL_MAX_CLK_RATES) && rcfg[i].rate; i++) {
    170		if (rcfg[i].rate == rate)
    171			return &rcfg[i];
    172	}
    173
    174	return NULL;
    175}
    176
    177static int skl_clk_change_status(struct skl_clk *clkdev,
    178				bool enable)
    179{
    180	struct skl_clk_rate_cfg_table *rcfg;
    181	int vbus_id, clk_type;
    182
    183	clk_type = skl_get_clk_type(clkdev->id);
    184	if (clk_type < 0)
    185		return clk_type;
    186
    187	vbus_id = skl_get_vbus_id(clkdev->id, clk_type);
    188	if (vbus_id < 0)
    189		return vbus_id;
    190
    191	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
    192						clkdev->rate);
    193	if (!rcfg)
    194		return -EINVAL;
    195
    196	return skl_send_clk_dma_control(clkdev->pdata->pvt_data, rcfg,
    197					vbus_id, clk_type, enable);
    198}
    199
    200static int skl_clk_prepare(struct clk_hw *hw)
    201{
    202	struct skl_clk *clkdev = to_skl_clk(hw);
    203
    204	return skl_clk_change_status(clkdev, true);
    205}
    206
    207static void skl_clk_unprepare(struct clk_hw *hw)
    208{
    209	struct skl_clk *clkdev = to_skl_clk(hw);
    210
    211	skl_clk_change_status(clkdev, false);
    212}
    213
    214static int skl_clk_set_rate(struct clk_hw *hw, unsigned long rate,
    215					unsigned long parent_rate)
    216{
    217	struct skl_clk *clkdev = to_skl_clk(hw);
    218	struct skl_clk_rate_cfg_table *rcfg;
    219	int clk_type;
    220
    221	if (!rate)
    222		return -EINVAL;
    223
    224	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
    225							rate);
    226	if (!rcfg)
    227		return -EINVAL;
    228
    229	clk_type = skl_get_clk_type(clkdev->id);
    230	if (clk_type < 0)
    231		return clk_type;
    232
    233	skl_fill_clk_ipc(rcfg, clk_type);
    234	clkdev->rate = rate;
    235
    236	return 0;
    237}
    238
    239static unsigned long skl_clk_recalc_rate(struct clk_hw *hw,
    240				unsigned long parent_rate)
    241{
    242	struct skl_clk *clkdev = to_skl_clk(hw);
    243
    244	if (clkdev->rate)
    245		return clkdev->rate;
    246
    247	return 0;
    248}
    249
    250/* Not supported by clk driver. Implemented to satisfy clk fw */
    251static long skl_clk_round_rate(struct clk_hw *hw, unsigned long rate,
    252			       unsigned long *parent_rate)
    253{
    254	return rate;
    255}
    256
    257/*
    258 * prepare/unprepare are used instead of enable/disable as IPC will be sent
    259 * in non-atomic context.
    260 */
    261static const struct clk_ops skl_clk_ops = {
    262	.prepare = skl_clk_prepare,
    263	.unprepare = skl_clk_unprepare,
    264	.set_rate = skl_clk_set_rate,
    265	.round_rate = skl_clk_round_rate,
    266	.recalc_rate = skl_clk_recalc_rate,
    267};
    268
    269static void unregister_parent_src_clk(struct skl_clk_parent *pclk,
    270					unsigned int id)
    271{
    272	while (id--) {
    273		clkdev_drop(pclk[id].lookup);
    274		clk_hw_unregister_fixed_rate(pclk[id].hw);
    275	}
    276}
    277
    278static void unregister_src_clk(struct skl_clk_data *dclk)
    279{
    280	while (dclk->avail_clk_cnt--)
    281		clkdev_drop(dclk->clk[dclk->avail_clk_cnt]->lookup);
    282}
    283
    284static int skl_register_parent_clks(struct device *dev,
    285			struct skl_clk_parent *parent,
    286			struct skl_clk_parent_src *pclk)
    287{
    288	int i, ret;
    289
    290	for (i = 0; i < SKL_MAX_CLK_SRC; i++) {
    291
    292		/* Register Parent clock */
    293		parent[i].hw = clk_hw_register_fixed_rate(dev, pclk[i].name,
    294				pclk[i].parent_name, 0, pclk[i].rate);
    295		if (IS_ERR(parent[i].hw)) {
    296			ret = PTR_ERR(parent[i].hw);
    297			goto err;
    298		}
    299
    300		parent[i].lookup = clkdev_hw_create(parent[i].hw, pclk[i].name,
    301									NULL);
    302		if (!parent[i].lookup) {
    303			clk_hw_unregister_fixed_rate(parent[i].hw);
    304			ret = -ENOMEM;
    305			goto err;
    306		}
    307	}
    308
    309	return 0;
    310err:
    311	unregister_parent_src_clk(parent, i);
    312	return ret;
    313}
    314
    315/* Assign fmt_config to clk_data */
    316static struct skl_clk *register_skl_clk(struct device *dev,
    317			struct skl_ssp_clk *clk,
    318			struct skl_clk_pdata *clk_pdata, int id)
    319{
    320	struct clk_init_data init;
    321	struct skl_clk *clkdev;
    322	int ret;
    323
    324	clkdev = devm_kzalloc(dev, sizeof(*clkdev), GFP_KERNEL);
    325	if (!clkdev)
    326		return ERR_PTR(-ENOMEM);
    327
    328	init.name = clk->name;
    329	init.ops = &skl_clk_ops;
    330	init.flags = CLK_SET_RATE_GATE;
    331	init.parent_names = &clk->parent_name;
    332	init.num_parents = 1;
    333	clkdev->hw.init = &init;
    334	clkdev->pdata = clk_pdata;
    335
    336	clkdev->id = id;
    337	ret = devm_clk_hw_register(dev, &clkdev->hw);
    338	if (ret) {
    339		clkdev = ERR_PTR(ret);
    340		return clkdev;
    341	}
    342
    343	clkdev->lookup = clkdev_hw_create(&clkdev->hw, init.name, NULL);
    344	if (!clkdev->lookup)
    345		clkdev = ERR_PTR(-ENOMEM);
    346
    347	return clkdev;
    348}
    349
    350static int skl_clk_dev_probe(struct platform_device *pdev)
    351{
    352	struct device *dev = &pdev->dev;
    353	struct device *parent_dev = dev->parent;
    354	struct skl_clk_parent_src *parent_clks;
    355	struct skl_clk_pdata *clk_pdata;
    356	struct skl_clk_data *data;
    357	struct skl_ssp_clk *clks;
    358	int ret, i;
    359
    360	clk_pdata = dev_get_platdata(&pdev->dev);
    361	parent_clks = clk_pdata->parent_clks;
    362	clks = clk_pdata->ssp_clks;
    363	if (!parent_clks || !clks)
    364		return -EIO;
    365
    366	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
    367	if (!data)
    368		return -ENOMEM;
    369
    370	/* Register Parent clock */
    371	ret = skl_register_parent_clks(parent_dev, data->parent, parent_clks);
    372	if (ret < 0)
    373		return ret;
    374
    375	for (i = 0; i < clk_pdata->num_clks; i++) {
    376		/*
    377		 * Only register valid clocks
    378		 * i.e. for which nhlt entry is present.
    379		 */
    380		if (clks[i].rate_cfg[0].rate == 0)
    381			continue;
    382
    383		data->clk[data->avail_clk_cnt] = register_skl_clk(dev,
    384				&clks[i], clk_pdata, i);
    385
    386		if (IS_ERR(data->clk[data->avail_clk_cnt])) {
    387			ret = PTR_ERR(data->clk[data->avail_clk_cnt]);
    388			goto err_unreg_skl_clk;
    389		}
    390
    391		data->avail_clk_cnt++;
    392	}
    393
    394	platform_set_drvdata(pdev, data);
    395
    396	return 0;
    397
    398err_unreg_skl_clk:
    399	unregister_src_clk(data);
    400	unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
    401
    402	return ret;
    403}
    404
    405static int skl_clk_dev_remove(struct platform_device *pdev)
    406{
    407	struct skl_clk_data *data;
    408
    409	data = platform_get_drvdata(pdev);
    410	unregister_src_clk(data);
    411	unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
    412
    413	return 0;
    414}
    415
    416static struct platform_driver skl_clk_driver = {
    417	.driver = {
    418		.name = "skl-ssp-clk",
    419	},
    420	.probe = skl_clk_dev_probe,
    421	.remove = skl_clk_dev_remove,
    422};
    423
    424module_platform_driver(skl_clk_driver);
    425
    426MODULE_DESCRIPTION("Skylake clock driver");
    427MODULE_AUTHOR("Jaikrishna Nemallapudi <jaikrishnax.nemallapudi@intel.com>");
    428MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
    429MODULE_LICENSE("GPL v2");
    430MODULE_ALIAS("platform:skl-ssp-clk");