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

imx-weim.c (9819B)


      1/*
      2 * EIM driver for Freescale's i.MX chips
      3 *
      4 * Copyright (C) 2013 Freescale Semiconductor, Inc.
      5 *
      6 * This file is licensed under the terms of the GNU General Public
      7 * License version 2. This program is licensed "as is" without any
      8 * warranty of any kind, whether express or implied.
      9 */
     10#include <linux/module.h>
     11#include <linux/clk.h>
     12#include <linux/io.h>
     13#include <linux/of_device.h>
     14#include <linux/mfd/syscon.h>
     15#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
     16#include <linux/regmap.h>
     17
     18struct imx_weim_devtype {
     19	unsigned int	cs_count;
     20	unsigned int	cs_regs_count;
     21	unsigned int	cs_stride;
     22	unsigned int	wcr_offset;
     23	unsigned int	wcr_bcm;
     24	unsigned int	wcr_cont_bclk;
     25};
     26
     27static const struct imx_weim_devtype imx1_weim_devtype = {
     28	.cs_count	= 6,
     29	.cs_regs_count	= 2,
     30	.cs_stride	= 0x08,
     31};
     32
     33static const struct imx_weim_devtype imx27_weim_devtype = {
     34	.cs_count	= 6,
     35	.cs_regs_count	= 3,
     36	.cs_stride	= 0x10,
     37};
     38
     39static const struct imx_weim_devtype imx50_weim_devtype = {
     40	.cs_count	= 4,
     41	.cs_regs_count	= 6,
     42	.cs_stride	= 0x18,
     43	.wcr_offset	= 0x90,
     44	.wcr_bcm	= BIT(0),
     45	.wcr_cont_bclk	= BIT(3),
     46};
     47
     48static const struct imx_weim_devtype imx51_weim_devtype = {
     49	.cs_count	= 6,
     50	.cs_regs_count	= 6,
     51	.cs_stride	= 0x18,
     52};
     53
     54#define MAX_CS_REGS_COUNT	6
     55#define MAX_CS_COUNT		6
     56#define OF_REG_SIZE		3
     57
     58struct cs_timing {
     59	bool is_applied;
     60	u32 regs[MAX_CS_REGS_COUNT];
     61};
     62
     63struct cs_timing_state {
     64	struct cs_timing cs[MAX_CS_COUNT];
     65};
     66
     67struct weim_priv {
     68	void __iomem *base;
     69	struct cs_timing_state timing_state;
     70};
     71
     72static const struct of_device_id weim_id_table[] = {
     73	/* i.MX1/21 */
     74	{ .compatible = "fsl,imx1-weim", .data = &imx1_weim_devtype, },
     75	/* i.MX25/27/31/35 */
     76	{ .compatible = "fsl,imx27-weim", .data = &imx27_weim_devtype, },
     77	/* i.MX50/53/6Q */
     78	{ .compatible = "fsl,imx50-weim", .data = &imx50_weim_devtype, },
     79	{ .compatible = "fsl,imx6q-weim", .data = &imx50_weim_devtype, },
     80	/* i.MX51 */
     81	{ .compatible = "fsl,imx51-weim", .data = &imx51_weim_devtype, },
     82	{ }
     83};
     84MODULE_DEVICE_TABLE(of, weim_id_table);
     85
     86static int imx_weim_gpr_setup(struct platform_device *pdev)
     87{
     88	struct device_node *np = pdev->dev.of_node;
     89	struct property *prop;
     90	const __be32 *p;
     91	struct regmap *gpr;
     92	u32 gprvals[4] = {
     93		05,	/* CS0(128M) CS1(0M)  CS2(0M)  CS3(0M)  */
     94		033,	/* CS0(64M)  CS1(64M) CS2(0M)  CS3(0M)  */
     95		0113,	/* CS0(64M)  CS1(32M) CS2(32M) CS3(0M)  */
     96		01111,	/* CS0(32M)  CS1(32M) CS2(32M) CS3(32M) */
     97	};
     98	u32 gprval = 0;
     99	u32 val;
    100	int cs = 0;
    101	int i = 0;
    102
    103	gpr = syscon_regmap_lookup_by_phandle(np, "fsl,weim-cs-gpr");
    104	if (IS_ERR(gpr)) {
    105		dev_dbg(&pdev->dev, "failed to find weim-cs-gpr\n");
    106		return 0;
    107	}
    108
    109	of_property_for_each_u32(np, "ranges", prop, p, val) {
    110		if (i % 4 == 0) {
    111			cs = val;
    112		} else if (i % 4 == 3 && val) {
    113			val = (val / SZ_32M) | 1;
    114			gprval |= val << cs * 3;
    115		}
    116		i++;
    117	}
    118
    119	if (i == 0 || i % 4)
    120		goto err;
    121
    122	for (i = 0; i < ARRAY_SIZE(gprvals); i++) {
    123		if (gprval == gprvals[i]) {
    124			/* Found it. Set up IOMUXC_GPR1[11:0] with it. */
    125			regmap_update_bits(gpr, IOMUXC_GPR1, 0xfff, gprval);
    126			return 0;
    127		}
    128	}
    129
    130err:
    131	dev_err(&pdev->dev, "Invalid 'ranges' configuration\n");
    132	return -EINVAL;
    133}
    134
    135/* Parse and set the timing for this device. */
    136static int weim_timing_setup(struct device *dev, struct device_node *np,
    137			     const struct imx_weim_devtype *devtype)
    138{
    139	u32 cs_idx, value[MAX_CS_REGS_COUNT];
    140	int i, ret;
    141	int reg_idx, num_regs;
    142	struct cs_timing *cst;
    143	struct weim_priv *priv;
    144	struct cs_timing_state *ts;
    145	void __iomem *base;
    146
    147	if (WARN_ON(devtype->cs_regs_count > MAX_CS_REGS_COUNT))
    148		return -EINVAL;
    149	if (WARN_ON(devtype->cs_count > MAX_CS_COUNT))
    150		return -EINVAL;
    151
    152	priv = dev_get_drvdata(dev);
    153	base = priv->base;
    154	ts = &priv->timing_state;
    155
    156	ret = of_property_read_u32_array(np, "fsl,weim-cs-timing",
    157					 value, devtype->cs_regs_count);
    158	if (ret)
    159		return ret;
    160
    161	/*
    162	 * the child node's "reg" property may contain multiple address ranges,
    163	 * extract the chip select for each.
    164	 */
    165	num_regs = of_property_count_elems_of_size(np, "reg", OF_REG_SIZE);
    166	if (num_regs < 0)
    167		return num_regs;
    168	if (!num_regs)
    169		return -EINVAL;
    170	for (reg_idx = 0; reg_idx < num_regs; reg_idx++) {
    171		/* get the CS index from this child node's "reg" property. */
    172		ret = of_property_read_u32_index(np, "reg",
    173					reg_idx * OF_REG_SIZE, &cs_idx);
    174		if (ret)
    175			break;
    176
    177		if (cs_idx >= devtype->cs_count)
    178			return -EINVAL;
    179
    180		/* prevent re-configuring a CS that's already been configured */
    181		cst = &ts->cs[cs_idx];
    182		if (cst->is_applied && memcmp(value, cst->regs,
    183					devtype->cs_regs_count * sizeof(u32))) {
    184			dev_err(dev, "fsl,weim-cs-timing conflict on %pOF", np);
    185			return -EINVAL;
    186		}
    187
    188		/* set the timing for WEIM */
    189		for (i = 0; i < devtype->cs_regs_count; i++)
    190			writel(value[i],
    191				base + cs_idx * devtype->cs_stride + i * 4);
    192		if (!cst->is_applied) {
    193			cst->is_applied = true;
    194			memcpy(cst->regs, value,
    195				devtype->cs_regs_count * sizeof(u32));
    196		}
    197	}
    198
    199	return 0;
    200}
    201
    202static int weim_parse_dt(struct platform_device *pdev)
    203{
    204	const struct of_device_id *of_id = of_match_device(weim_id_table,
    205							   &pdev->dev);
    206	const struct imx_weim_devtype *devtype = of_id->data;
    207	struct device_node *child;
    208	int ret, have_child = 0;
    209	struct weim_priv *priv;
    210	void __iomem *base;
    211	u32 reg;
    212
    213	if (devtype == &imx50_weim_devtype) {
    214		ret = imx_weim_gpr_setup(pdev);
    215		if (ret)
    216			return ret;
    217	}
    218
    219	priv = dev_get_drvdata(&pdev->dev);
    220	base = priv->base;
    221
    222	if (of_property_read_bool(pdev->dev.of_node, "fsl,burst-clk-enable")) {
    223		if (devtype->wcr_bcm) {
    224			reg = readl(base + devtype->wcr_offset);
    225			reg |= devtype->wcr_bcm;
    226
    227			if (of_property_read_bool(pdev->dev.of_node,
    228						"fsl,continuous-burst-clk")) {
    229				if (devtype->wcr_cont_bclk) {
    230					reg |= devtype->wcr_cont_bclk;
    231				} else {
    232					dev_err(&pdev->dev,
    233						"continuous burst clk not supported.\n");
    234					return -EINVAL;
    235				}
    236			}
    237
    238			writel(reg, base + devtype->wcr_offset);
    239		} else {
    240			dev_err(&pdev->dev, "burst clk mode not supported.\n");
    241			return -EINVAL;
    242		}
    243	}
    244
    245	for_each_available_child_of_node(pdev->dev.of_node, child) {
    246		ret = weim_timing_setup(&pdev->dev, child, devtype);
    247		if (ret)
    248			dev_warn(&pdev->dev, "%pOF set timing failed.\n",
    249				child);
    250		else
    251			have_child = 1;
    252	}
    253
    254	if (have_child)
    255		ret = of_platform_default_populate(pdev->dev.of_node,
    256						   NULL, &pdev->dev);
    257	if (ret)
    258		dev_err(&pdev->dev, "%pOF fail to create devices.\n",
    259			pdev->dev.of_node);
    260	return ret;
    261}
    262
    263static int weim_probe(struct platform_device *pdev)
    264{
    265	struct weim_priv *priv;
    266	struct resource *res;
    267	struct clk *clk;
    268	void __iomem *base;
    269	int ret;
    270
    271	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    272	if (!priv)
    273		return -ENOMEM;
    274
    275	/* get the resource */
    276	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    277	base = devm_ioremap_resource(&pdev->dev, res);
    278	if (IS_ERR(base))
    279		return PTR_ERR(base);
    280
    281	priv->base = base;
    282	dev_set_drvdata(&pdev->dev, priv);
    283
    284	/* get the clock */
    285	clk = devm_clk_get(&pdev->dev, NULL);
    286	if (IS_ERR(clk))
    287		return PTR_ERR(clk);
    288
    289	ret = clk_prepare_enable(clk);
    290	if (ret)
    291		return ret;
    292
    293	/* parse the device node */
    294	ret = weim_parse_dt(pdev);
    295	if (ret)
    296		clk_disable_unprepare(clk);
    297	else
    298		dev_info(&pdev->dev, "Driver registered.\n");
    299
    300	return ret;
    301}
    302
    303#if IS_ENABLED(CONFIG_OF_DYNAMIC)
    304static int of_weim_notify(struct notifier_block *nb, unsigned long action,
    305			  void *arg)
    306{
    307	const struct imx_weim_devtype *devtype;
    308	struct of_reconfig_data *rd = arg;
    309	const struct of_device_id *of_id;
    310	struct platform_device *pdev;
    311	int ret = NOTIFY_OK;
    312
    313	switch (of_reconfig_get_state_change(action, rd)) {
    314	case OF_RECONFIG_CHANGE_ADD:
    315		of_id = of_match_node(weim_id_table, rd->dn->parent);
    316		if (!of_id)
    317			return NOTIFY_OK; /* not for us */
    318
    319		devtype = of_id->data;
    320
    321		pdev = of_find_device_by_node(rd->dn->parent);
    322		if (!pdev) {
    323			pr_err("%s: could not find platform device for '%pOF'\n",
    324				__func__, rd->dn->parent);
    325
    326			return notifier_from_errno(-EINVAL);
    327		}
    328
    329		if (weim_timing_setup(&pdev->dev, rd->dn, devtype))
    330			dev_warn(&pdev->dev,
    331				 "Failed to setup timing for '%pOF'\n", rd->dn);
    332
    333		if (!of_node_check_flag(rd->dn, OF_POPULATED)) {
    334			if (!of_platform_device_create(rd->dn, NULL, &pdev->dev)) {
    335				dev_err(&pdev->dev,
    336					"Failed to create child device '%pOF'\n",
    337					rd->dn);
    338				ret = notifier_from_errno(-EINVAL);
    339			}
    340		}
    341
    342		platform_device_put(pdev);
    343
    344		break;
    345	case OF_RECONFIG_CHANGE_REMOVE:
    346		if (!of_node_check_flag(rd->dn, OF_POPULATED))
    347			return NOTIFY_OK; /* device already destroyed */
    348
    349		of_id = of_match_node(weim_id_table, rd->dn->parent);
    350		if (!of_id)
    351			return NOTIFY_OK; /* not for us */
    352
    353		pdev = of_find_device_by_node(rd->dn);
    354		if (!pdev) {
    355			pr_err("Could not find platform device for '%pOF'\n",
    356				rd->dn);
    357
    358			ret = notifier_from_errno(-EINVAL);
    359		} else {
    360			of_platform_device_destroy(&pdev->dev, NULL);
    361			platform_device_put(pdev);
    362		}
    363
    364		break;
    365	default:
    366		break;
    367	}
    368
    369	return ret;
    370}
    371
    372static struct notifier_block weim_of_notifier = {
    373	.notifier_call = of_weim_notify,
    374};
    375#endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */
    376
    377static struct platform_driver weim_driver = {
    378	.driver = {
    379		.name		= "imx-weim",
    380		.of_match_table	= weim_id_table,
    381	},
    382	.probe = weim_probe,
    383};
    384
    385static int __init weim_init(void)
    386{
    387#if IS_ENABLED(CONFIG_OF_DYNAMIC)
    388	WARN_ON(of_reconfig_notifier_register(&weim_of_notifier));
    389#endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */
    390
    391	return platform_driver_register(&weim_driver);
    392}
    393module_init(weim_init);
    394
    395static void __exit weim_exit(void)
    396{
    397#if IS_ENABLED(CONFIG_OF_DYNAMIC)
    398	of_reconfig_notifier_unregister(&weim_of_notifier);
    399#endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */
    400
    401	return platform_driver_unregister(&weim_driver);
    402
    403}
    404module_exit(weim_exit);
    405
    406MODULE_AUTHOR("Freescale Semiconductor Inc.");
    407MODULE_DESCRIPTION("i.MX EIM Controller Driver");
    408MODULE_LICENSE("GPL");