mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00
i.MX8MN has different speed grading definition as below, it has 4 bits
to define speed grading, add support for it.
SPEED_GRADE[3:0] MHz
0000 2300
0001 2200
0010 2100
0011 2000
0100 1900
0101 1800
0110 1700
0111 1600
1000 1500
1001 1400
1010 1300
1011 1200
1100 1100
1101 1000
1110 900
1111 800
Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
Reviewed-by: Leonard Crestez <leonard.crestez@nxp.com>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
105 lines
3.0 KiB
C
105 lines
3.0 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright 2019 NXP
|
|
*/
|
|
|
|
#include <linux/cpu.h>
|
|
#include <linux/err.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/nvmem-consumer.h>
|
|
#include <linux/of.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pm_opp.h>
|
|
#include <linux/slab.h>
|
|
|
|
#define OCOTP_CFG3_SPEED_GRADE_SHIFT 8
|
|
#define OCOTP_CFG3_SPEED_GRADE_MASK (0x3 << 8)
|
|
#define IMX8MN_OCOTP_CFG3_SPEED_GRADE_MASK (0xf << 8)
|
|
#define OCOTP_CFG3_MKT_SEGMENT_SHIFT 6
|
|
#define OCOTP_CFG3_MKT_SEGMENT_MASK (0x3 << 6)
|
|
|
|
/* cpufreq-dt device registered by imx-cpufreq-dt */
|
|
static struct platform_device *cpufreq_dt_pdev;
|
|
static struct opp_table *cpufreq_opp_table;
|
|
|
|
static int imx_cpufreq_dt_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *cpu_dev = get_cpu_device(0);
|
|
u32 cell_value, supported_hw[2];
|
|
int speed_grade, mkt_segment;
|
|
int ret;
|
|
|
|
ret = nvmem_cell_read_u32(cpu_dev, "speed_grade", &cell_value);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (of_machine_is_compatible("fsl,imx8mn"))
|
|
speed_grade = (cell_value & IMX8MN_OCOTP_CFG3_SPEED_GRADE_MASK)
|
|
>> OCOTP_CFG3_SPEED_GRADE_SHIFT;
|
|
else
|
|
speed_grade = (cell_value & OCOTP_CFG3_SPEED_GRADE_MASK)
|
|
>> OCOTP_CFG3_SPEED_GRADE_SHIFT;
|
|
mkt_segment = (cell_value & OCOTP_CFG3_MKT_SEGMENT_MASK) >> OCOTP_CFG3_MKT_SEGMENT_SHIFT;
|
|
|
|
/*
|
|
* Early samples without fuses written report "0 0" which means
|
|
* consumer segment and minimum speed grading.
|
|
*
|
|
* According to datasheet minimum speed grading is not supported for
|
|
* consumer parts so clamp to 1 to avoid warning for "no OPPs"
|
|
*
|
|
* Applies to i.MX8M series SoCs.
|
|
*/
|
|
if (mkt_segment == 0 && speed_grade == 0 && (
|
|
of_machine_is_compatible("fsl,imx8mm") ||
|
|
of_machine_is_compatible("fsl,imx8mn") ||
|
|
of_machine_is_compatible("fsl,imx8mq")))
|
|
speed_grade = 1;
|
|
|
|
supported_hw[0] = BIT(speed_grade);
|
|
supported_hw[1] = BIT(mkt_segment);
|
|
dev_info(&pdev->dev, "cpu speed grade %d mkt segment %d supported-hw %#x %#x\n",
|
|
speed_grade, mkt_segment, supported_hw[0], supported_hw[1]);
|
|
|
|
cpufreq_opp_table = dev_pm_opp_set_supported_hw(cpu_dev, supported_hw, 2);
|
|
if (IS_ERR(cpufreq_opp_table)) {
|
|
ret = PTR_ERR(cpufreq_opp_table);
|
|
dev_err(&pdev->dev, "Failed to set supported opp: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
cpufreq_dt_pdev = platform_device_register_data(
|
|
&pdev->dev, "cpufreq-dt", -1, NULL, 0);
|
|
if (IS_ERR(cpufreq_dt_pdev)) {
|
|
dev_pm_opp_put_supported_hw(cpufreq_opp_table);
|
|
ret = PTR_ERR(cpufreq_dt_pdev);
|
|
dev_err(&pdev->dev, "Failed to register cpufreq-dt: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int imx_cpufreq_dt_remove(struct platform_device *pdev)
|
|
{
|
|
platform_device_unregister(cpufreq_dt_pdev);
|
|
dev_pm_opp_put_supported_hw(cpufreq_opp_table);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver imx_cpufreq_dt_driver = {
|
|
.probe = imx_cpufreq_dt_probe,
|
|
.remove = imx_cpufreq_dt_remove,
|
|
.driver = {
|
|
.name = "imx-cpufreq-dt",
|
|
},
|
|
};
|
|
module_platform_driver(imx_cpufreq_dt_driver);
|
|
|
|
MODULE_ALIAS("platform:imx-cpufreq-dt");
|
|
MODULE_DESCRIPTION("Freescale i.MX cpufreq speed grading driver");
|
|
MODULE_LICENSE("GPL v2");
|