Skip to content
Snippets Groups Projects
Commit b01a3ba2 authored by Clemens Terasa's avatar Clemens Terasa
Browse files

imx6-spreadspectrum: Initial commit for imx6-spreadspectrum

Adds the G&F iMX6 spread spectrum module.
Pulled version c8281e68 from the G&F
rocko port.

Does not build yet due to kernel 4.1 to 5.10 incompatibilities.

BCS 746-000453
parent c42e4c5e
No related branches found
No related tags found
1 merge request!112imx6[ull]guf: Add kernel-module-imx6-spreadspectrum
SUMMARY = "Kernel module to enable spread spectrum on IMX6"
LICENSE = "GPLv2"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-2.0;md5=801f80980d171dd6425610833a22dbe6"
inherit module-base kernel-module-split
inherit module
SRC_URI = "file://Makefile \
file://imx6-spreadspectrum.c \
"
S = "${WORKDIR}"
do_configure_prepend () {
sed -i "s/SVNVERSION/${PV}/g" ${S}/Makefile
}
# The inherit of module.bbclass will automatically name module packages with
# "kernel-module-" prefix as required by the oe-core build environment.
RRECOMMENDS_${PN} = "kernel-module-${PN}"
KERNEL_MODULE_AUTOLOAD += "${PN}"
obj-m += imx6-spreadspectrum.o
SRC := $(shell pwd)
ccflags-y := $(ccflags-y) -D VERSION="\"SVNVERSION\""
all:
$(MAKE) -C $(KERNEL_SRC) M=$(SRC)
modules_install:
$(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install
clean:
rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c
rm -f Module.markers Module.symvers modules.order
rm -rf .tmp_versions Modules.symvers
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/mfd/syscon.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
//#define DEBUG 1
/* PLL2_528 defines */
#define ANADIG_PLL_528_DIV_SELECT (1)
#define ANADIG_PLL_528_SYS_SS_STOP_OFFSET (16)
#define ANADIG_PLL_528_SYS_SS_STOP_MASK (0xFFFF << ANADIG_PLL_528_SYS_SS_STOP_OFFSET)
#define ANADIG_PLL_528_SYS_SS_STEP_OFFSET (0)
#define ANADIG_PLL_528_SYS_SS_STEP_MASK (0x7FFF)
#define ANADIG_PLL_528_SYS_SS_ENABLE_OFFSET (15)
#define ANADIG_PLL_528_SYS_SS_ENABLE (0x1 << ANADIG_PLL_528_SYS_SS_ENABLE_OFFSET)
#define ANADIG_PLL_528_DENOM_MASK (0x3FFFFFFF)
#define PLL_SETREG_OFFSET 0x4
#define PLL_CLRREG_OFFSET 0x8
#define PLL_TOGGLE_OFFSET 0x0C
#define PLL_NUM_DIV_OFFSET 0x10
#define PLL_DENOM_DIV_OFFSET 0x20
#define PLL_528_SS_OFFSET 0x10
#define PLL_528_NUM_DIV_OFFSET 0x20
#define PLL_528_DENOM_DIV_OFFSET 0x30
#define PLL2_528_OFFSET 0x30
/*
* PLL2 spread spectrum can be configured via /proc/mx6_spreadspectrum
* using the following syntax:
*
* "<step>,<stop>,<denom>,<enabled>" (e.g. "1,250,400,1")
*
* spread spectrum range = stop/denom * 24MHz
* modulation frequency = step/(2*stop) * 24MHz
* frequency step = step/denom * 24MHz
*/
#define PROC_FS_MAX_LEN 22
#define PROC_FS_NAME "mx6_spreadspectrum"
#define FREF 24000000
#define MODULENAME "imx6_spreadspectrum"
struct spreadspectrum_params {
u32 step;
u32 stop;
u32 denom;
u32 enabled;
};
//================================================================================
// Helper functions
//================================================================================
static void __iomem * find_anatop(void)
{
struct device_node *np;
void __iomem *anatop;
np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop"); //imx6q-anatop also works for imx6ul(l)
anatop = of_iomap(np, 0);
WARN_ON(!anatop);
return anatop;
}
/*Function imx6_spreadspectrum_write_params
* -----------------------------------
* writes spread spectrum parameters to hardware
*/
static int imx6_spreadspectrum_write_params(struct spreadspectrum_params *params)
{
void __iomem *anatop_base = 0;
uint32_t sys_ss, denom, stop, step, enabled;
anatop_base = find_anatop();
step = ((params->step << ANADIG_PLL_528_SYS_SS_STEP_OFFSET) & ANADIG_PLL_528_SYS_SS_STEP_MASK) >> ANADIG_PLL_528_SYS_SS_STEP_OFFSET;
stop = ((params->stop << ANADIG_PLL_528_SYS_SS_STOP_OFFSET) & ANADIG_PLL_528_SYS_SS_STOP_MASK) >> ANADIG_PLL_528_SYS_SS_STOP_OFFSET;
denom = params->denom & ANADIG_PLL_528_DENOM_MASK;
enabled = params->enabled;
sys_ss = (step << ANADIG_PLL_528_SYS_SS_STEP_OFFSET) | (stop << ANADIG_PLL_528_SYS_SS_STOP_OFFSET) | (enabled << ANADIG_PLL_528_SYS_SS_ENABLE_OFFSET);
pr_debug("%s: %s %d: Writing to registers: step=%d, stop=%d, denom=%d, enabled=%d. Resulting raw value for PLL_SYS_SS=0x%X\n", MODULENAME, __func__, __LINE__, step, stop, denom, enabled, sys_ss);
/* Disable spread spectrum mode */
__raw_writel((__raw_readl(anatop_base + PLL2_528_OFFSET + PLL_528_SS_OFFSET) & ~ANADIG_PLL_528_SYS_SS_ENABLE), anatop_base + PLL2_528_OFFSET + + PLL_528_SS_OFFSET);
/* Write new values */
__raw_writel(sys_ss, anatop_base + PLL2_528_OFFSET + PLL_528_SS_OFFSET);
__raw_writel(denom, anatop_base + PLL2_528_OFFSET + PLL_528_DENOM_DIV_OFFSET);
/* Enable spread spectrum mode */
if (enabled)
__raw_writel((__raw_readl(anatop_base + PLL2_528_OFFSET + PLL_528_SS_OFFSET) | ANADIG_PLL_528_SYS_SS_ENABLE), anatop_base + PLL2_528_OFFSET + + PLL_528_SS_OFFSET);
return 0;
}
/*Function imx6_spreadspectrum_get_of_property
* --------------------------------------
* reads spread spectrum parameters from device tree:
*
* params: struct to store the parameters read from device tree
*/
static int imx6_spreadspectrum_get_of_property(struct platform_device *pdev,
struct spreadspectrum_params *params)
{
struct device_node *np = pdev->dev.of_node;
int err;
u32 step, stop, denom, enabled;
err = of_property_read_u32(np, "step", &step);
if (err) {
dev_warn(&pdev->dev, "Failed to get parameter 'step'\n");
return err;
}
err = of_property_read_u32(np, "stop", &stop);
if (err) {
dev_warn(&pdev->dev, "Failed to get parameter 'stop'\n");
return err;
}
err = of_property_read_u32(np, "denom", &denom);
if (err) {
dev_warn(&pdev->dev, "Failed to get parameter 'denom'\n");
return err;
}
err = of_property_read_u32(np, "enabled", &enabled);
if (err) {
dev_warn(&pdev->dev, "Failed to get parameter 'enabled'\n");
return err;
}
params->step = step;
params->stop = stop;
params->denom = denom;
params->enabled = enabled;
dev_dbg(&pdev->dev, "Read parameters from device tree: step=%u, stop=%u, denom=%u, enabled=%u\n", params->step, params->stop, params->denom, params->enabled);
return err;
}
/*Function imx6_spreadspectrum_write_proc
* -------------------------------
* reads spread spectrum parameters from procfs entry
*/
static int imx6_spreadspectrum_write_proc(struct file *filp, const char *buf,
size_t count, loff_t *offp)
{
int ret;
char str[PROC_FS_MAX_LEN];
unsigned int procfs_buffer_size = 0;
int ints[5];
struct spreadspectrum_params params;
procfs_buffer_size = count;
if (procfs_buffer_size > PROC_FS_MAX_LEN )
procfs_buffer_size = PROC_FS_MAX_LEN;
pr_debug("%s: %s %d, %p %d\n", MODULENAME, __func__, __LINE__, buf, count);
if (copy_from_user(str, buf, procfs_buffer_size)) {
printk("spreadsprectrum_get_proc: failed at copy_from_user\n");
return -EFAULT;
}
str[procfs_buffer_size - 1] = '\0';
get_options(str, 5, ints);
if (ints[0] != 4) {
ret = -EINVAL;
} else {
params.step = ints[1];
params.stop = ints[2];
params.denom = ints[3];
params.enabled = ints[4];
pr_debug("%s: Read parameters from procfs: step=%u, stop=%u, denom=%u, enabled=%u\n", MODULENAME, params.step, params.stop, params.denom, params.enabled);
imx6_spreadspectrum_write_params(&params);
ret = procfs_buffer_size;
}
pr_debug("%s: %s %d: ret %d\n", MODULENAME, __func__, __LINE__, ret);
return ret;
}
/*Function imx6_spreadspectrum_read_proc
* ------------------------------
* reads spread spectrum paramaters from hardware and writes them to procfs
*/
static int imx6_spreadspectrum_read_proc(struct file *filp, char *buf, size_t count,
loff_t *offp)
{
static int state = 0;
int ret;
char buffer[PROC_FS_MAX_LEN];
uint32_t sys_ss, denom, stop, step, enabled;
void __iomem *anatop_base = 0;
anatop_base = find_anatop();
if ( state != 0) {
state = 0;
return 0;
}
sys_ss = __raw_readl(anatop_base + PLL2_528_OFFSET + PLL_528_SS_OFFSET);
denom = __raw_readl(anatop_base + PLL2_528_OFFSET + PLL_528_DENOM_DIV_OFFSET);
stop = (sys_ss & ANADIG_PLL_528_SYS_SS_STOP_MASK) >> ANADIG_PLL_528_SYS_SS_STOP_OFFSET;
step = (sys_ss & ANADIG_PLL_528_SYS_SS_STEP_MASK) >> ANADIG_PLL_528_SYS_SS_STEP_OFFSET;
enabled = (sys_ss & ANADIG_PLL_528_SYS_SS_ENABLE) ? 1 : 0;
ret = snprintf(buffer, PROC_FS_MAX_LEN, "%u,%u,%u,%u\n", step, stop, denom, enabled);
pr_debug("%s: %s %d- %s\n", MODULENAME, __func__, ret, buffer);
if (ret < 0) goto error;
if (ret >= PROC_FS_MAX_LEN) ret = PROC_FS_MAX_LEN;
count = ret;
ret = copy_to_user(buf, buffer, count + 1);
if (ret) goto error;
state = 1;
return count + 1;
error:
pr_err("%s Error %d\n", __func__, ret);
return ret;
}
static const struct file_operations imx6_spreadspectrum_fops = {
.owner = THIS_MODULE,
.read = imx6_spreadspectrum_read_proc,
.write = imx6_spreadspectrum_write_proc,
};
/* Function spreadsprectrum_create_proc_fs_entry:
* ---------------------------------------------
* creates a procfs entry to read/write spread spectrum parameters to/from
*/
int imx6_spreadsprectrum_create_proc_fs_entry(void)
{
/* Create procfs entry for spread spectrum */
struct proc_dir_entry *entry = proc_create(PROC_FS_NAME, 0666, NULL, &imx6_spreadspectrum_fops);
if (!entry) {
pr_debug("%s: Could not create /proc/%s\n", MODULENAME, PROC_FS_NAME);
return -ENODEV;
} else {
pr_debug("%s: /proc/%s created\n", MODULENAME, PROC_FS_NAME);
}
return 0;
}
static int imx6_spreadspectrum_probe(struct platform_device *pdev)
{
int ret;
struct spreadspectrum_params *params;
dev_dbg(&pdev->dev, "PROBING GUF IMX6 SPREAD SPECTRUM DRIVER\n");
params = devm_kzalloc(&pdev->dev, sizeof(struct spreadspectrum_params),
GFP_KERNEL);
if (!params)
return -ENOMEM;
pdev->dev.platform_data = params;
ret = imx6_spreadspectrum_get_of_property(pdev, params);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to get spreadspectrum parameters\n");
goto error;
}
ret = imx6_spreadspectrum_write_params(params);
return ret;
error:
pr_err("%s Error %d\n", __func__, ret);
devm_kfree(&pdev->dev, params);
return ret;
}
static int imx6_spreadspectrum_remove(struct platform_device *pdev)
{
int ret;
struct spreadspectrum_params *params;
params = (struct spreadspectrum_params*)pdev->dev.platform_data;
dev_dbg(&pdev->dev, "unbinding device\n");
/* disable spread spectrum */
params->enabled = 0;
ret = imx6_spreadspectrum_write_params(params);
dev_dbg(&pdev->dev, "spread spectrum disabled\n");
devm_kfree(&pdev->dev, params);
/* Remove procfs entry for spread spectrum */
remove_proc_entry(PROC_FS_NAME, NULL);
dev_dbg(&pdev->dev, "removed /proc/%s\n", PROC_FS_NAME);
return 0;
}
static struct of_device_id imx6_spreadspectrum_match[] = {
{ .compatible = "guf,imx6-spreadspectrum"},
{},
};
MODULE_DEVICE_TABLE(of, imx6_spreadspectrum_match);
static struct platform_driver imx6_spreadspectrum_drvr = {
.probe = imx6_spreadspectrum_probe,
.remove = imx6_spreadspectrum_remove,
.driver = {
.name = "imx6-spreadspectrum",
.owner = THIS_MODULE,
.of_match_table = imx6_spreadspectrum_match,
},
};
static int __init imx6_spreadspectrum_init(void)
{
int ret;
pr_debug("%s: INIT DRIVER\n", MODULENAME);
ret = imx6_spreadsprectrum_create_proc_fs_entry();
if (ret) {
pr_warn("%s: Error while creating spread spectrum proc fs entry: %d\n", __func__, ret);
}
return platform_driver_register(&imx6_spreadspectrum_drvr);
}
static void __exit imx6_spreadspectrum_exit(void)
{
pr_debug("%s: EXIT DRIVER\n", MODULENAME);
platform_driver_unregister(&imx6_spreadspectrum_drvr);
}
module_init(imx6_spreadspectrum_init);
module_exit(imx6_spreadspectrum_exit);
MODULE_LICENSE("GPL");
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment