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

mpc8610_hpcd.c (13609B)


      1// SPDX-License-Identifier: GPL-2.0
      2//
      3// Freescale MPC8610HPCD ALSA SoC Machine driver
      4//
      5// Author: Timur Tabi <timur@freescale.com>
      6//
      7// Copyright 2007-2010 Freescale Semiconductor, Inc.
      8
      9#include <linux/module.h>
     10#include <linux/interrupt.h>
     11#include <linux/fsl/guts.h>
     12#include <linux/of_address.h>
     13#include <linux/of_device.h>
     14#include <linux/slab.h>
     15#include <sound/soc.h>
     16
     17#include "fsl_dma.h"
     18#include "fsl_ssi.h"
     19#include "fsl_utils.h"
     20
     21/* There's only one global utilities register */
     22static phys_addr_t guts_phys;
     23
     24/**
     25 * mpc8610_hpcd_data: machine-specific ASoC device data
     26 *
     27 * This structure contains data for a single sound platform device on an
     28 * MPC8610 HPCD.  Some of the data is taken from the device tree.
     29 */
     30struct mpc8610_hpcd_data {
     31	struct snd_soc_dai_link dai[2];
     32	struct snd_soc_card card;
     33	unsigned int dai_format;
     34	unsigned int codec_clk_direction;
     35	unsigned int cpu_clk_direction;
     36	unsigned int clk_frequency;
     37	unsigned int ssi_id;		/* 0 = SSI1, 1 = SSI2, etc */
     38	unsigned int dma_id[2];		/* 0 = DMA1, 1 = DMA2, etc */
     39	unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/
     40	char codec_dai_name[DAI_NAME_SIZE];
     41	char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */
     42};
     43
     44/**
     45 * mpc8610_hpcd_machine_probe: initialize the board
     46 *
     47 * This function is used to initialize the board-specific hardware.
     48 *
     49 * Here we program the DMACR and PMUXCR registers.
     50 */
     51static int mpc8610_hpcd_machine_probe(struct snd_soc_card *card)
     52{
     53	struct mpc8610_hpcd_data *machine_data =
     54		container_of(card, struct mpc8610_hpcd_data, card);
     55	struct ccsr_guts __iomem *guts;
     56
     57	guts = ioremap(guts_phys, sizeof(struct ccsr_guts));
     58	if (!guts) {
     59		dev_err(card->dev, "could not map global utilities\n");
     60		return -ENOMEM;
     61	}
     62
     63	/* Program the signal routing between the SSI and the DMA */
     64	guts_set_dmacr(guts, machine_data->dma_id[0],
     65		       machine_data->dma_channel_id[0],
     66		       CCSR_GUTS_DMACR_DEV_SSI);
     67	guts_set_dmacr(guts, machine_data->dma_id[1],
     68		       machine_data->dma_channel_id[1],
     69		       CCSR_GUTS_DMACR_DEV_SSI);
     70
     71	guts_set_pmuxcr_dma(guts, machine_data->dma_id[0],
     72			    machine_data->dma_channel_id[0], 0);
     73	guts_set_pmuxcr_dma(guts, machine_data->dma_id[1],
     74			    machine_data->dma_channel_id[1], 0);
     75
     76	switch (machine_data->ssi_id) {
     77	case 0:
     78		clrsetbits_be32(&guts->pmuxcr,
     79			CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_SSI);
     80		break;
     81	case 1:
     82		clrsetbits_be32(&guts->pmuxcr,
     83			CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_SSI);
     84		break;
     85	}
     86
     87	iounmap(guts);
     88
     89	return 0;
     90}
     91
     92/**
     93 * mpc8610_hpcd_startup: program the board with various hardware parameters
     94 *
     95 * This function takes board-specific information, like clock frequencies
     96 * and serial data formats, and passes that information to the codec and
     97 * transport drivers.
     98 */
     99static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream)
    100{
    101	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    102	struct mpc8610_hpcd_data *machine_data =
    103		container_of(rtd->card, struct mpc8610_hpcd_data, card);
    104	struct device *dev = rtd->card->dev;
    105	int ret = 0;
    106
    107	/* Tell the codec driver what the serial protocol is. */
    108	ret = snd_soc_dai_set_fmt(asoc_rtd_to_codec(rtd, 0), machine_data->dai_format);
    109	if (ret < 0) {
    110		dev_err(dev, "could not set codec driver audio format\n");
    111		return ret;
    112	}
    113
    114	/*
    115	 * Tell the codec driver what the MCLK frequency is, and whether it's
    116	 * a slave or master.
    117	 */
    118	ret = snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, 0), 0,
    119				     machine_data->clk_frequency,
    120				     machine_data->codec_clk_direction);
    121	if (ret < 0) {
    122		dev_err(dev, "could not set codec driver clock params\n");
    123		return ret;
    124	}
    125
    126	return 0;
    127}
    128
    129/**
    130 * mpc8610_hpcd_machine_remove: Remove the sound device
    131 *
    132 * This function is called to remove the sound device for one SSI.  We
    133 * de-program the DMACR and PMUXCR register.
    134 */
    135static int mpc8610_hpcd_machine_remove(struct snd_soc_card *card)
    136{
    137	struct mpc8610_hpcd_data *machine_data =
    138		container_of(card, struct mpc8610_hpcd_data, card);
    139	struct ccsr_guts __iomem *guts;
    140
    141	guts = ioremap(guts_phys, sizeof(struct ccsr_guts));
    142	if (!guts) {
    143		dev_err(card->dev, "could not map global utilities\n");
    144		return -ENOMEM;
    145	}
    146
    147	/* Restore the signal routing */
    148
    149	guts_set_dmacr(guts, machine_data->dma_id[0],
    150		       machine_data->dma_channel_id[0], 0);
    151	guts_set_dmacr(guts, machine_data->dma_id[1],
    152		       machine_data->dma_channel_id[1], 0);
    153
    154	switch (machine_data->ssi_id) {
    155	case 0:
    156		clrsetbits_be32(&guts->pmuxcr,
    157			CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA);
    158		break;
    159	case 1:
    160		clrsetbits_be32(&guts->pmuxcr,
    161			CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA);
    162		break;
    163	}
    164
    165	iounmap(guts);
    166
    167	return 0;
    168}
    169
    170/**
    171 * mpc8610_hpcd_ops: ASoC machine driver operations
    172 */
    173static const struct snd_soc_ops mpc8610_hpcd_ops = {
    174	.startup = mpc8610_hpcd_startup,
    175};
    176
    177/**
    178 * mpc8610_hpcd_probe: platform probe function for the machine driver
    179 *
    180 * Although this is a machine driver, the SSI node is the "master" node with
    181 * respect to audio hardware connections.  Therefore, we create a new ASoC
    182 * device for each new SSI node that has a codec attached.
    183 */
    184static int mpc8610_hpcd_probe(struct platform_device *pdev)
    185{
    186	struct device *dev = pdev->dev.parent;
    187	/* ssi_pdev is the platform device for the SSI node that probed us */
    188	struct platform_device *ssi_pdev = to_platform_device(dev);
    189	struct device_node *np = ssi_pdev->dev.of_node;
    190	struct device_node *codec_np = NULL;
    191	struct mpc8610_hpcd_data *machine_data;
    192	struct snd_soc_dai_link_component *comp;
    193	int ret;
    194	const char *sprop;
    195	const u32 *iprop;
    196
    197	/* Find the codec node for this SSI. */
    198	codec_np = of_parse_phandle(np, "codec-handle", 0);
    199	if (!codec_np) {
    200		dev_err(dev, "invalid codec node\n");
    201		return -EINVAL;
    202	}
    203
    204	machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL);
    205	if (!machine_data) {
    206		ret = -ENOMEM;
    207		goto error_alloc;
    208	}
    209
    210	comp = devm_kzalloc(&pdev->dev, 6 * sizeof(*comp), GFP_KERNEL);
    211	if (!comp) {
    212		ret = -ENOMEM;
    213		goto error_alloc;
    214	}
    215
    216	machine_data->dai[0].cpus	= &comp[0];
    217	machine_data->dai[0].codecs	= &comp[1];
    218	machine_data->dai[0].platforms	= &comp[2];
    219
    220	machine_data->dai[0].num_cpus		= 1;
    221	machine_data->dai[0].num_codecs		= 1;
    222	machine_data->dai[0].num_platforms	= 1;
    223
    224	machine_data->dai[1].cpus	= &comp[3];
    225	machine_data->dai[1].codecs	= &comp[4];
    226	machine_data->dai[1].platforms	= &comp[5];
    227
    228	machine_data->dai[1].num_cpus		= 1;
    229	machine_data->dai[1].num_codecs		= 1;
    230	machine_data->dai[1].num_platforms	= 1;
    231
    232	machine_data->dai[0].cpus->dai_name = dev_name(&ssi_pdev->dev);
    233	machine_data->dai[0].ops = &mpc8610_hpcd_ops;
    234
    235	/* ASoC core can match codec with device node */
    236	machine_data->dai[0].codecs->of_node = codec_np;
    237
    238	/* The DAI name from the codec (snd_soc_dai_driver.name) */
    239	machine_data->dai[0].codecs->dai_name = "cs4270-hifi";
    240
    241	/* We register two DAIs per SSI, one for playback and the other for
    242	 * capture.  Currently, we only support codecs that have one DAI for
    243	 * both playback and capture.
    244	 */
    245	memcpy(&machine_data->dai[1], &machine_data->dai[0],
    246	       sizeof(struct snd_soc_dai_link));
    247
    248	/* Get the device ID */
    249	iprop = of_get_property(np, "cell-index", NULL);
    250	if (!iprop) {
    251		dev_err(&pdev->dev, "cell-index property not found\n");
    252		ret = -EINVAL;
    253		goto error;
    254	}
    255	machine_data->ssi_id = be32_to_cpup(iprop);
    256
    257	/* Get the serial format and clock direction. */
    258	sprop = of_get_property(np, "fsl,mode", NULL);
    259	if (!sprop) {
    260		dev_err(&pdev->dev, "fsl,mode property not found\n");
    261		ret = -EINVAL;
    262		goto error;
    263	}
    264
    265	if (strcasecmp(sprop, "i2s-slave") == 0) {
    266		machine_data->dai_format =
    267			SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBP_CFP;
    268		machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
    269		machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
    270
    271		/* In i2s-slave mode, the codec has its own clock source, so we
    272		 * need to get the frequency from the device tree and pass it to
    273		 * the codec driver.
    274		 */
    275		iprop = of_get_property(codec_np, "clock-frequency", NULL);
    276		if (!iprop || !*iprop) {
    277			dev_err(&pdev->dev, "codec bus-frequency "
    278				"property is missing or invalid\n");
    279			ret = -EINVAL;
    280			goto error;
    281		}
    282		machine_data->clk_frequency = be32_to_cpup(iprop);
    283	} else if (strcasecmp(sprop, "i2s-master") == 0) {
    284		machine_data->dai_format =
    285			SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC;
    286		machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
    287		machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
    288	} else if (strcasecmp(sprop, "lj-slave") == 0) {
    289		machine_data->dai_format =
    290			SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBP_CFP;
    291		machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
    292		machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
    293	} else if (strcasecmp(sprop, "lj-master") == 0) {
    294		machine_data->dai_format =
    295			SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBC_CFC;
    296		machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
    297		machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
    298	} else if (strcasecmp(sprop, "rj-slave") == 0) {
    299		machine_data->dai_format =
    300			SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBP_CFP;
    301		machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
    302		machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
    303	} else if (strcasecmp(sprop, "rj-master") == 0) {
    304		machine_data->dai_format =
    305			SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBC_CFC;
    306		machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
    307		machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
    308	} else if (strcasecmp(sprop, "ac97-slave") == 0) {
    309		machine_data->dai_format =
    310			SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBP_CFP;
    311		machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
    312		machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
    313	} else if (strcasecmp(sprop, "ac97-master") == 0) {
    314		machine_data->dai_format =
    315			SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBC_CFC;
    316		machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
    317		machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
    318	} else {
    319		dev_err(&pdev->dev,
    320			"unrecognized fsl,mode property '%s'\n", sprop);
    321		ret = -EINVAL;
    322		goto error;
    323	}
    324
    325	if (!machine_data->clk_frequency) {
    326		dev_err(&pdev->dev, "unknown clock frequency\n");
    327		ret = -EINVAL;
    328		goto error;
    329	}
    330
    331	/* Find the playback DMA channel to use. */
    332	machine_data->dai[0].platforms->name = machine_data->platform_name[0];
    333	ret = fsl_asoc_get_dma_channel(np, "fsl,playback-dma",
    334				       &machine_data->dai[0],
    335				       &machine_data->dma_channel_id[0],
    336				       &machine_data->dma_id[0]);
    337	if (ret) {
    338		dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n");
    339		goto error;
    340	}
    341
    342	/* Find the capture DMA channel to use. */
    343	machine_data->dai[1].platforms->name = machine_data->platform_name[1];
    344	ret = fsl_asoc_get_dma_channel(np, "fsl,capture-dma",
    345				       &machine_data->dai[1],
    346				       &machine_data->dma_channel_id[1],
    347				       &machine_data->dma_id[1]);
    348	if (ret) {
    349		dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n");
    350		goto error;
    351	}
    352
    353	/* Initialize our DAI data structure.  */
    354	machine_data->dai[0].stream_name = "playback";
    355	machine_data->dai[1].stream_name = "capture";
    356	machine_data->dai[0].name = machine_data->dai[0].stream_name;
    357	machine_data->dai[1].name = machine_data->dai[1].stream_name;
    358
    359	machine_data->card.probe = mpc8610_hpcd_machine_probe;
    360	machine_data->card.remove = mpc8610_hpcd_machine_remove;
    361	machine_data->card.name = pdev->name; /* The platform driver name */
    362	machine_data->card.owner = THIS_MODULE;
    363	machine_data->card.dev = &pdev->dev;
    364	machine_data->card.num_links = 2;
    365	machine_data->card.dai_link = machine_data->dai;
    366
    367	/* Register with ASoC */
    368	ret = snd_soc_register_card(&machine_data->card);
    369	if (ret) {
    370		dev_err(&pdev->dev, "could not register card\n");
    371		goto error;
    372	}
    373
    374	of_node_put(codec_np);
    375
    376	return 0;
    377
    378error:
    379	kfree(machine_data);
    380error_alloc:
    381	of_node_put(codec_np);
    382	return ret;
    383}
    384
    385/**
    386 * mpc8610_hpcd_remove: remove the platform device
    387 *
    388 * This function is called when the platform device is removed.
    389 */
    390static int mpc8610_hpcd_remove(struct platform_device *pdev)
    391{
    392	struct snd_soc_card *card = platform_get_drvdata(pdev);
    393	struct mpc8610_hpcd_data *machine_data =
    394		container_of(card, struct mpc8610_hpcd_data, card);
    395
    396	snd_soc_unregister_card(card);
    397	kfree(machine_data);
    398
    399	return 0;
    400}
    401
    402static struct platform_driver mpc8610_hpcd_driver = {
    403	.probe = mpc8610_hpcd_probe,
    404	.remove = mpc8610_hpcd_remove,
    405	.driver = {
    406		/* The name must match 'compatible' property in the device tree,
    407		 * in lowercase letters.
    408		 */
    409		.name = "snd-soc-mpc8610hpcd",
    410	},
    411};
    412
    413/**
    414 * mpc8610_hpcd_init: machine driver initialization.
    415 *
    416 * This function is called when this module is loaded.
    417 */
    418static int __init mpc8610_hpcd_init(void)
    419{
    420	struct device_node *guts_np;
    421	struct resource res;
    422
    423	pr_info("Freescale MPC8610 HPCD ALSA SoC machine driver\n");
    424
    425	/* Get the physical address of the global utilities registers */
    426	guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts");
    427	if (of_address_to_resource(guts_np, 0, &res)) {
    428		pr_err("mpc8610-hpcd: missing/invalid global utilities node\n");
    429		of_node_put(guts_np);
    430		return -EINVAL;
    431	}
    432	guts_phys = res.start;
    433	of_node_put(guts_np);
    434
    435	return platform_driver_register(&mpc8610_hpcd_driver);
    436}
    437
    438/**
    439 * mpc8610_hpcd_exit: machine driver exit
    440 *
    441 * This function is called when this driver is unloaded.
    442 */
    443static void __exit mpc8610_hpcd_exit(void)
    444{
    445	platform_driver_unregister(&mpc8610_hpcd_driver);
    446}
    447
    448module_init(mpc8610_hpcd_init);
    449module_exit(mpc8610_hpcd_exit);
    450
    451MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
    452MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC machine driver");
    453MODULE_LICENSE("GPL v2");