blob: 7203e260852ea49352e66db44a508f55533b8112 [file] [log] [blame]
/*
* Copyright (C) 2015 Freescale Semiconductor, Inc.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include "clk.h"
#include "common.h"
#include "hardware.h"
#define NORMAL_AHB_FREQ 132000000
#define LOW_AHB_FREQ 24000000
#define LOW_OCRAM_FREQ 24000000
#define LOW_DISPLAY_AXI_FREQ 66000000
static struct device *low_system_bus_dev;
static struct clk *ahb, *ipg, *periph, *periph_pre,
*periph_clk2, *periph_clk2_sel, *osc, *ocram_sel, *ocram, *display_podf,
*ecspi_sel, *uart_sel, *gpu_core_sel, *gpu_axi_sel, *pll2;
#ifdef CONFIG_PM_SLEEP
static int imx6sx_low_system_bus_suspend(struct device *dev)
{
/* make sure AHB&IPG divider is correct before changing periph clk parent */
clk_set_rate(ipg, LOW_AHB_FREQ / 2);
clk_set_rate(ahb, LOW_AHB_FREQ / 3);
imx_clk_set_parent(periph, periph_pre);
clk_set_rate(ahb, NORMAL_AHB_FREQ);
clk_set_rate(ipg, NORMAL_AHB_FREQ / 2);
return 0;
}
static int imx6sx_low_system_bus_resume(struct device *dev)
{
imx_clk_set_parent(periph, periph_clk2);
clk_set_rate(ahb, LOW_AHB_FREQ);
clk_set_rate(ipg, LOW_AHB_FREQ / 2);
return 0;
}
#endif
static int imx_low_system_bus_probe(struct platform_device *pdev)
{
low_system_bus_dev = &pdev->dev;
ahb = devm_clk_get(low_system_bus_dev, "ahb");
ipg = devm_clk_get(low_system_bus_dev, "ipg");
periph = devm_clk_get(low_system_bus_dev, "periph");
periph_pre = devm_clk_get(low_system_bus_dev, "periph_pre");
periph_clk2 = devm_clk_get(low_system_bus_dev, "periph_clk2");
periph_clk2_sel = devm_clk_get(low_system_bus_dev, "periph_clk2_sel");
osc = devm_clk_get(low_system_bus_dev, "osc");
ocram_sel = devm_clk_get(low_system_bus_dev, "ocram_sel");
ocram = devm_clk_get(low_system_bus_dev, "ocram");
display_podf = devm_clk_get(low_system_bus_dev, "display_podf");
ecspi_sel = devm_clk_get(low_system_bus_dev, "ecspi_sel");
uart_sel = devm_clk_get(low_system_bus_dev, "uart_sel");
gpu_core_sel = devm_clk_get(low_system_bus_dev, "gpu_core_sel");
gpu_axi_sel = devm_clk_get(low_system_bus_dev, "gpu_axi_sel");
pll2 = devm_clk_get(low_system_bus_dev, "pll2");
if (IS_ERR(ahb) || IS_ERR(ipg)
|| IS_ERR(periph) || IS_ERR(periph_pre)
|| IS_ERR(periph_clk2) || IS_ERR(periph_clk2_sel)
|| IS_ERR(osc) || IS_ERR(ocram_sel) || IS_ERR(ocram)
|| IS_ERR(display_podf) || IS_ERR(ecspi_sel) || IS_ERR(uart_sel)
|| IS_ERR(gpu_core_sel) || IS_ERR(gpu_axi_sel) || IS_ERR(pll2)) {
dev_err(low_system_bus_dev, "failed to get all necessary clks!\n");
return -ENOENT;
}
imx_clk_set_parent(periph_clk2_sel, osc);
imx_clk_set_parent(periph, periph_clk2);
clk_set_rate(ahb, LOW_AHB_FREQ);
clk_set_rate(ipg, LOW_AHB_FREQ / 2);
imx_clk_set_parent(ocram_sel, periph);
clk_set_rate(ocram, LOW_OCRAM_FREQ);
clk_set_rate(display_podf, LOW_DISPLAY_AXI_FREQ);
imx_clk_set_parent(uart_sel, osc);
imx_clk_set_parent(ecspi_sel, osc);
//avoid gpu clock reconfiguration for now. keeping the code.
//imx_clk_set_parent(gpu_core_sel, pll2);
//imx_clk_set_parent(gpu_axi_sel, pll2);
pr_info("set AHB/IPG to 24MHz/12MHz, OCRAM to 24MHz, DISPLAY_AXI to 66MHz\n");
pr_info("imx low system bus driver probed!\n");
return 0;
}
#ifdef CONFIG_PM_SLEEP
static SIMPLE_DEV_PM_OPS(imx6sx_low_system_bus_pm_ops,
imx6sx_low_system_bus_suspend, imx6sx_low_system_bus_resume);
#endif
static const struct of_device_id imx_low_system_bus_ids[] = {
{ .compatible = "fsl,imx6sx-low_system_bus" },
};
MODULE_DEVICE_TABLE(of, imx_low_system_bus_ids);
static struct platform_driver imx_low_system_bus_platdrv = {
.driver = {
.name = "imx-low-system-bus",
.owner = THIS_MODULE,
#ifdef CONFIG_PM_SLEEP
.pm = &imx6sx_low_system_bus_pm_ops,
#endif
.of_match_table = imx_low_system_bus_ids,
},
.probe = imx_low_system_bus_probe,
};
static int __init imx6_low_system_bus_init(void)
{
return platform_driver_probe(&imx_low_system_bus_platdrv, imx_low_system_bus_probe);
}
fs_initcall(imx6_low_system_bus_init);
MODULE_AUTHOR("Anson Huang <b20788@freescale.com>");
MODULE_DESCRIPTION("Freescale i.MX low system bus driver");
MODULE_LICENSE("GPL");