-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This patch add SMI(Smart Multimedia Interface) driver. This driver is responsible to enable/disable iommu and control the power domain and clocks of each local arbiter. Signed-off-by: Yong Wu <yong.wu@mediatek.com> Tested-by: Philipp Zabel <p.zabel@pengutronix.de> Reviewed-by: Daniel Kurtz <djkurtz@chromium.org> Tested-by: Daniel Kurtz <djkurtz@chromium.org> Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com> Signed-off-by: Joerg Roedel <jroedel@suse.de>
- Loading branch information
Yong Wu
authored and
Joerg Roedel
committed
Feb 25, 2016
1 parent
fb6e2ce
commit cc8bbe1
Showing
4 changed files
with
340 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,273 @@ | ||
/* | ||
* Copyright (c) 2015-2016 MediaTek Inc. | ||
* Author: Yong Wu <yong.wu@mediatek.com> | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
*/ | ||
#include <linux/clk.h> | ||
#include <linux/component.h> | ||
#include <linux/device.h> | ||
#include <linux/err.h> | ||
#include <linux/io.h> | ||
#include <linux/of.h> | ||
#include <linux/of_platform.h> | ||
#include <linux/platform_device.h> | ||
#include <linux/pm_runtime.h> | ||
#include <soc/mediatek/smi.h> | ||
|
||
#define SMI_LARB_MMU_EN 0xf00 | ||
|
||
struct mtk_smi { | ||
struct device *dev; | ||
struct clk *clk_apb, *clk_smi; | ||
}; | ||
|
||
struct mtk_smi_larb { /* larb: local arbiter */ | ||
struct mtk_smi smi; | ||
void __iomem *base; | ||
struct device *smi_common_dev; | ||
u32 *mmu; | ||
}; | ||
|
||
static int mtk_smi_enable(const struct mtk_smi *smi) | ||
{ | ||
int ret; | ||
|
||
ret = pm_runtime_get_sync(smi->dev); | ||
if (ret < 0) | ||
return ret; | ||
|
||
ret = clk_prepare_enable(smi->clk_apb); | ||
if (ret) | ||
goto err_put_pm; | ||
|
||
ret = clk_prepare_enable(smi->clk_smi); | ||
if (ret) | ||
goto err_disable_apb; | ||
|
||
return 0; | ||
|
||
err_disable_apb: | ||
clk_disable_unprepare(smi->clk_apb); | ||
err_put_pm: | ||
pm_runtime_put_sync(smi->dev); | ||
return ret; | ||
} | ||
|
||
static void mtk_smi_disable(const struct mtk_smi *smi) | ||
{ | ||
clk_disable_unprepare(smi->clk_smi); | ||
clk_disable_unprepare(smi->clk_apb); | ||
pm_runtime_put_sync(smi->dev); | ||
} | ||
|
||
int mtk_smi_larb_get(struct device *larbdev) | ||
{ | ||
struct mtk_smi_larb *larb = dev_get_drvdata(larbdev); | ||
struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev); | ||
int ret; | ||
|
||
/* Enable the smi-common's power and clocks */ | ||
ret = mtk_smi_enable(common); | ||
if (ret) | ||
return ret; | ||
|
||
/* Enable the larb's power and clocks */ | ||
ret = mtk_smi_enable(&larb->smi); | ||
if (ret) { | ||
mtk_smi_disable(common); | ||
return ret; | ||
} | ||
|
||
/* Configure the iommu info for this larb */ | ||
writel(*larb->mmu, larb->base + SMI_LARB_MMU_EN); | ||
|
||
return 0; | ||
} | ||
|
||
void mtk_smi_larb_put(struct device *larbdev) | ||
{ | ||
struct mtk_smi_larb *larb = dev_get_drvdata(larbdev); | ||
struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev); | ||
|
||
/* | ||
* Don't de-configure the iommu info for this larb since there may be | ||
* several modules in this larb. | ||
* The iommu info will be reset after power off. | ||
*/ | ||
|
||
mtk_smi_disable(&larb->smi); | ||
mtk_smi_disable(common); | ||
} | ||
|
||
static int | ||
mtk_smi_larb_bind(struct device *dev, struct device *master, void *data) | ||
{ | ||
struct mtk_smi_larb *larb = dev_get_drvdata(dev); | ||
struct mtk_smi_iommu *smi_iommu = data; | ||
unsigned int i; | ||
|
||
for (i = 0; i < smi_iommu->larb_nr; i++) { | ||
if (dev == smi_iommu->larb_imu[i].dev) { | ||
/* The 'mmu' may be updated in iommu-attach/detach. */ | ||
larb->mmu = &smi_iommu->larb_imu[i].mmu; | ||
return 0; | ||
} | ||
} | ||
return -ENODEV; | ||
} | ||
|
||
static void | ||
mtk_smi_larb_unbind(struct device *dev, struct device *master, void *data) | ||
{ | ||
/* Do nothing as the iommu is always enabled. */ | ||
} | ||
|
||
static const struct component_ops mtk_smi_larb_component_ops = { | ||
.bind = mtk_smi_larb_bind, | ||
.unbind = mtk_smi_larb_unbind, | ||
}; | ||
|
||
static int mtk_smi_larb_probe(struct platform_device *pdev) | ||
{ | ||
struct mtk_smi_larb *larb; | ||
struct resource *res; | ||
struct device *dev = &pdev->dev; | ||
struct device_node *smi_node; | ||
struct platform_device *smi_pdev; | ||
|
||
if (!dev->pm_domain) | ||
return -EPROBE_DEFER; | ||
|
||
larb = devm_kzalloc(dev, sizeof(*larb), GFP_KERNEL); | ||
if (!larb) | ||
return -ENOMEM; | ||
|
||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
larb->base = devm_ioremap_resource(dev, res); | ||
if (IS_ERR(larb->base)) | ||
return PTR_ERR(larb->base); | ||
|
||
larb->smi.clk_apb = devm_clk_get(dev, "apb"); | ||
if (IS_ERR(larb->smi.clk_apb)) | ||
return PTR_ERR(larb->smi.clk_apb); | ||
|
||
larb->smi.clk_smi = devm_clk_get(dev, "smi"); | ||
if (IS_ERR(larb->smi.clk_smi)) | ||
return PTR_ERR(larb->smi.clk_smi); | ||
larb->smi.dev = dev; | ||
|
||
smi_node = of_parse_phandle(dev->of_node, "mediatek,smi", 0); | ||
if (!smi_node) | ||
return -EINVAL; | ||
|
||
smi_pdev = of_find_device_by_node(smi_node); | ||
of_node_put(smi_node); | ||
if (smi_pdev) { | ||
larb->smi_common_dev = &smi_pdev->dev; | ||
} else { | ||
dev_err(dev, "Failed to get the smi_common device\n"); | ||
return -EINVAL; | ||
} | ||
|
||
pm_runtime_enable(dev); | ||
platform_set_drvdata(pdev, larb); | ||
return component_add(dev, &mtk_smi_larb_component_ops); | ||
} | ||
|
||
static int mtk_smi_larb_remove(struct platform_device *pdev) | ||
{ | ||
pm_runtime_disable(&pdev->dev); | ||
component_del(&pdev->dev, &mtk_smi_larb_component_ops); | ||
return 0; | ||
} | ||
|
||
static const struct of_device_id mtk_smi_larb_of_ids[] = { | ||
{ .compatible = "mediatek,mt8173-smi-larb",}, | ||
{} | ||
}; | ||
|
||
static struct platform_driver mtk_smi_larb_driver = { | ||
.probe = mtk_smi_larb_probe, | ||
.remove = mtk_smi_larb_remove, | ||
.driver = { | ||
.name = "mtk-smi-larb", | ||
.of_match_table = mtk_smi_larb_of_ids, | ||
} | ||
}; | ||
|
||
static int mtk_smi_common_probe(struct platform_device *pdev) | ||
{ | ||
struct device *dev = &pdev->dev; | ||
struct mtk_smi *common; | ||
|
||
if (!dev->pm_domain) | ||
return -EPROBE_DEFER; | ||
|
||
common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL); | ||
if (!common) | ||
return -ENOMEM; | ||
common->dev = dev; | ||
|
||
common->clk_apb = devm_clk_get(dev, "apb"); | ||
if (IS_ERR(common->clk_apb)) | ||
return PTR_ERR(common->clk_apb); | ||
|
||
common->clk_smi = devm_clk_get(dev, "smi"); | ||
if (IS_ERR(common->clk_smi)) | ||
return PTR_ERR(common->clk_smi); | ||
|
||
pm_runtime_enable(dev); | ||
platform_set_drvdata(pdev, common); | ||
return 0; | ||
} | ||
|
||
static int mtk_smi_common_remove(struct platform_device *pdev) | ||
{ | ||
pm_runtime_disable(&pdev->dev); | ||
return 0; | ||
} | ||
|
||
static const struct of_device_id mtk_smi_common_of_ids[] = { | ||
{ .compatible = "mediatek,mt8173-smi-common", }, | ||
{} | ||
}; | ||
|
||
static struct platform_driver mtk_smi_common_driver = { | ||
.probe = mtk_smi_common_probe, | ||
.remove = mtk_smi_common_remove, | ||
.driver = { | ||
.name = "mtk-smi-common", | ||
.of_match_table = mtk_smi_common_of_ids, | ||
} | ||
}; | ||
|
||
static int __init mtk_smi_init(void) | ||
{ | ||
int ret; | ||
|
||
ret = platform_driver_register(&mtk_smi_common_driver); | ||
if (ret != 0) { | ||
pr_err("Failed to register SMI driver\n"); | ||
return ret; | ||
} | ||
|
||
ret = platform_driver_register(&mtk_smi_larb_driver); | ||
if (ret != 0) { | ||
pr_err("Failed to register SMI-LARB driver\n"); | ||
goto err_unreg_smi; | ||
} | ||
return ret; | ||
|
||
err_unreg_smi: | ||
platform_driver_unregister(&mtk_smi_common_driver); | ||
return ret; | ||
} | ||
subsys_initcall(mtk_smi_init); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/* | ||
* Copyright (c) 2015-2016 MediaTek Inc. | ||
* Author: Yong Wu <yong.wu@mediatek.com> | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
*/ | ||
#ifndef MTK_IOMMU_SMI_H | ||
#define MTK_IOMMU_SMI_H | ||
|
||
#include <linux/bitops.h> | ||
#include <linux/device.h> | ||
|
||
#ifdef CONFIG_MTK_SMI | ||
|
||
#define MTK_LARB_NR_MAX 8 | ||
|
||
#define MTK_SMI_MMU_EN(port) BIT(port) | ||
|
||
struct mtk_smi_larb_iommu { | ||
struct device *dev; | ||
unsigned int mmu; | ||
}; | ||
|
||
struct mtk_smi_iommu { | ||
unsigned int larb_nr; | ||
struct mtk_smi_larb_iommu larb_imu[MTK_LARB_NR_MAX]; | ||
}; | ||
|
||
/* | ||
* mtk_smi_larb_get: Enable the power domain and clocks for this local arbiter. | ||
* It also initialize some basic setting(like iommu). | ||
* mtk_smi_larb_put: Disable the power domain and clocks for this local arbiter. | ||
* Both should be called in non-atomic context. | ||
* | ||
* Returns 0 if successful, negative on failure. | ||
*/ | ||
int mtk_smi_larb_get(struct device *larbdev); | ||
void mtk_smi_larb_put(struct device *larbdev); | ||
|
||
#else | ||
|
||
static inline int mtk_smi_larb_get(struct device *larbdev) | ||
{ | ||
return 0; | ||
} | ||
|
||
static inline void mtk_smi_larb_put(struct device *larbdev) { } | ||
|
||
#endif | ||
|
||
#endif |