summaryrefslogtreecommitdiff
path: root/drivers/misc/fm_2351
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/misc/fm_2351
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/misc/fm_2351')
-rw-r--r--drivers/misc/fm_2351/Kconfig11
-rw-r--r--drivers/misc/fm_2351/Makefile4
-rw-r--r--drivers/misc/fm_2351/sr2351_fm_ctrl.c473
-rw-r--r--drivers/misc/fm_2351/sr2351_fm_ctrl.h274
-rw-r--r--drivers/misc/fm_2351/sr2351_fm_rf.c822
5 files changed, 1584 insertions, 0 deletions
diff --git a/drivers/misc/fm_2351/Kconfig b/drivers/misc/fm_2351/Kconfig
new file mode 100644
index 00000000..6b926be8
--- /dev/null
+++ b/drivers/misc/fm_2351/Kconfig
@@ -0,0 +1,11 @@
+config SR2351_FM
+ tristate "SPRD 2351 FM Support"
+ ---help---
+ Say Y to enable built-in wireless MAC controller in the
+ Spreadtrum SC88xx based System-on-Chip devices.
+
+config FM_SEEK_STEP_50KHZ
+ tristate "Trout FM Seek Step 50KHz Support "
+ default n
+ help
+ If you say yes here you can support Step 50KHz for Trout FM. \ No newline at end of file
diff --git a/drivers/misc/fm_2351/Makefile b/drivers/misc/fm_2351/Makefile
new file mode 100644
index 00000000..0bd4a888
--- /dev/null
+++ b/drivers/misc/fm_2351/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_SR2351_FM) := trout_fm.o
+trout_fm-y := sr2351_fm_ctrl.o \
+ sr2351_fm_rf.o
+
diff --git a/drivers/misc/fm_2351/sr2351_fm_ctrl.c b/drivers/misc/fm_2351/sr2351_fm_ctrl.c
new file mode 100644
index 00000000..d98c5f03
--- /dev/null
+++ b/drivers/misc/fm_2351/sr2351_fm_ctrl.c
@@ -0,0 +1,473 @@
+#include <linux/miscdevice.h>
+#include <linux/sysfs.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/ioctl.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <asm/io.h>
+#include "sr2351_fm_ctrl.h"
+
+#ifdef CONFIG_OF
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#endif
+
+#define SR2351_FM_VERSION "v0.9"
+static u32 g_volume = 0;
+
+int sr2351_fm_set_volume(u32 iarg)
+{
+ SR2351_PRINT("FM set volume : %i.", iarg);
+
+ g_volume = iarg;
+ if(0 == iarg)
+ {
+ sr2351_fm_mute();
+ }
+ else
+ {
+ sr2351_fm_unmute();
+ }
+
+ return 0;
+}
+
+u32 sr2351_fm_get_volume(void)
+{
+ SR2351_PRINT("FM get volume.");
+ return g_volume;
+}
+
+
+
+int sr2351_fm_open(struct inode *inode, struct file *filep)
+{
+ int ret = -EINVAL;
+ int status;
+
+ SR2351_PRINT("start open shark fm module...");
+/*
+ if (get_suspend_state() != PM_SUSPEND_ON)
+ {
+ SR2351_PRINT("The system is suspending!");
+ return -2;
+ }
+*/
+/*
+ ret = nonseekable_open(inode, filep);
+ if (ret < 0) {
+ SR2351_PRINT("open misc device failed.");
+ return ret;
+ }
+*/
+
+ ret = sr2351_fm_init();
+ if (ret < 0) {
+ SR2351_PRINT("sr2351_fm_init failed!");
+ return ret;
+ }
+
+ ret = sr2351_fm_get_status(&status);
+ if (ret < 0) {
+ SR2351_PRINT("sr2351_read_fm_en failed!");
+ return ret;
+ }
+ if (status) {
+ SR2351_PRINT("sr2351 fm have been opened.");
+ } else {
+ ret = sr2351_fm_en();
+ if (ret < 0) {
+ SR2351_PRINT("sr2351_fm_en failed!");
+ return ret;
+ }
+ }
+
+ SR2351_PRINT("Open shark fm module success.");
+
+ return 0;
+}
+
+long sr2351_fm_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ long ret = 0;
+ u32 iarg = 0;
+ u32 buf[4] = { 0 };
+
+ SR2351_PRINT("FM IOCTL: 0x%x.", cmd);
+
+ switch (cmd) {
+ case FM_IOCTL_ENABLE:
+ if (copy_from_user(&iarg, argp, sizeof(iarg)) || iarg > 1)
+ ret = -EFAULT;
+
+ if (iarg == 1)
+ ret = sr2351_fm_en();
+ else
+ ret = sr2351_fm_dis();
+
+ break;
+
+ case FM_IOCTL_GET_ENABLE:
+ ret = sr2351_fm_get_status(&iarg);
+ if (copy_to_user(argp, &iarg, sizeof(iarg)))
+ ret = -EFAULT;
+
+ break;
+
+ case FM_IOCTL_SET_TUNE:
+ if (copy_from_user(&iarg, argp, sizeof(iarg)))
+ return -EFAULT;
+ sr2351_fm_mute();
+ ret = sr2351_fm_set_tune(iarg);
+ sr2351_fm_unmute();
+ break;
+
+ case FM_IOCTL_GET_FREQ:
+ ret = sr2351_fm_get_frequency(&iarg);
+ if (copy_to_user(argp, &iarg, sizeof(iarg)))
+ ret = -EFAULT;
+ break;
+
+ case FM_IOCTL_SEARCH:
+ if (copy_from_user(buf, argp, sizeof(buf)))
+ ret = -EFAULT;
+ sr2351_fm_mute();
+ ret = sr2351_fm_seek(buf[0], /* start frequency */
+ buf[1], /* seek direction */
+ buf[2], /* time out */
+ &buf[3]); /* frequency found */
+
+ if (copy_to_user(argp, buf, sizeof(buf)))
+ ret = -EFAULT;
+
+ break;
+
+ case FM_IOCTL_STOP_SEARCH:
+ ret = sr2351_fm_stop_seek();
+ break;
+
+ case FM_IOCTL_MUTE:
+ break;
+
+ case FM_IOCTL_SET_VOLUME:
+ if (copy_from_user(&iarg, argp, sizeof(iarg)))
+ ret = -EFAULT;
+
+ ret = sr2351_fm_set_volume(iarg);
+ break;
+
+ case FM_IOCTL_GET_VOLUME:
+ iarg = sr2351_fm_get_volume();
+ if (copy_to_user(argp, &iarg, sizeof(iarg)))
+ ret = -EFAULT;
+
+ break;
+
+ case FM_IOCTL_CONFIG:
+ if (copy_from_user(&iarg, argp, sizeof(iarg)))
+ ret = -EFAULT;
+
+ SR2351_PRINT("\n\nFM FM_IOCTL_CONFIG set %d\n\n", iarg);
+
+ break;
+
+ case FM_IOCTL_GET_RSSI:
+ ret = sr2351_fm_get_rssi(&iarg);
+ if(copy_to_user(argp,&iarg,sizeof(iarg)))
+ ret = -EFAULT;
+ break;
+
+ case FM_IOCTL_SET_BAND:
+ if (copy_from_user(buf, argp, sizeof(buf)))
+ ret = -EFAULT;
+ ret = sr2351_fm_set_band(buf[0],//min freq
+ buf[1]);//max freq
+ break;
+
+ case FM_IOCTL_CHECK_STATUS:
+ ret = sr2351_fm_check_status(&iarg);
+ if(copy_to_user(argp,&iarg,sizeof(iarg)))
+ ret = -EFAULT;
+ break;
+
+ default:
+ SR2351_PRINT("Unknown FM IOCTL!");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+int sr2351_fm_release(struct inode *inode, struct file *filep)
+{
+ SR2351_PRINT("sr2351_fm_misc_release");
+
+ sr2351_fm_deinit();
+
+ return 0;
+}
+
+const struct file_operations sr2351_fm_misc_fops = {
+ .owner = THIS_MODULE,
+ .open = sr2351_fm_open,
+ .unlocked_ioctl = sr2351_fm_ioctl,
+ .release = sr2351_fm_release,
+};
+
+struct miscdevice sr2351_fm_misc_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = SR2351_FM_DEV_NAME,
+ .fops = &sr2351_fm_misc_fops,
+};
+
+#ifdef CONFIG_OF
+static struct sr2351_fm_addr
+{
+ unsigned long fm_base;
+ unsigned long apb_base;
+ unsigned long pmu_base;
+ unsigned long aonckg_base;
+ unsigned long pin_base;
+}sr2351_fm_base;
+
+unsigned long rf2351_fm_get_fm_base(void)
+{
+ return sr2351_fm_base.fm_base;
+}
+unsigned long rf2351_fm_get_apb_base(void)
+{
+ return sr2351_fm_base.apb_base;
+}
+
+unsigned long rf2351_fm_get_pmu_base(void)
+{
+ return sr2351_fm_base.pmu_base;
+}
+
+unsigned long rf2351_fm_get_aonckg_base(void)
+{
+ return sr2351_fm_base.aonckg_base;
+}
+
+unsigned long rf2351_fm_get_pin_base(void)
+{
+ return sr2351_fm_base.pin_base;
+}
+
+static int sr2351_fm_parse_dts(struct device_node *np)
+{
+ int ret;
+ struct resource res;
+
+ //np = of_find_node_by_name(NULL, "sprd_fm");
+ if (NULL == np)
+ {
+ SR2351_PRINT("Can't get the sprd_fm node!\n");
+ return -ENODEV;
+ }
+ SR2351_PRINT(" find the fm_sr2351 node!\n");
+
+ ret = of_address_to_resource(np, 0, &res);
+ if (ret < 0)
+ {
+ SR2351_PRINT("Can't get the fm reg base!\n");
+ return -EIO;
+ }
+ sr2351_fm_base.fm_base = (unsigned long)ioremap_nocache(res.start, resource_size(&res));
+ if(!sr2351_fm_base.fm_base)
+ {
+ BUG();
+ }
+ SR2351_PRINT("fm reg base is 0x%x\n", sr2351_fm_base.fm_base);
+
+ ret = of_address_to_resource(np, 1, &res);
+ if (ret < 0)
+ {
+ SR2351_PRINT("Can't get the apb reg base!\n");
+ return -EIO;
+ }
+ sr2351_fm_base.apb_base = (unsigned long)ioremap_nocache(res.start, resource_size(&res));
+ if(!sr2351_fm_base.apb_base)
+ {
+ BUG();
+ }
+ SR2351_PRINT("apb reg base is 0x%x\n", sr2351_fm_base.apb_base);
+
+ ret = of_address_to_resource(np, 2, &res);
+ if (ret < 0)
+ {
+ SR2351_PRINT("Can't get the pmu reg base!\n");
+ return -EIO;
+ }
+ sr2351_fm_base.pmu_base = (unsigned long)ioremap_nocache(res.start, resource_size(&res));
+ if(!sr2351_fm_base.pmu_base)
+ {
+ BUG();
+ }
+ SR2351_PRINT("pmu reg base is 0x%x\n", sr2351_fm_base.pmu_base);
+
+ ret = of_address_to_resource(np, 3, &res);
+ if (ret < 0)
+ {
+ SR2351_PRINT("Can't get the aonckg reg base!\n");
+ return -EIO;
+ }
+ sr2351_fm_base.aonckg_base = (unsigned long)ioremap_nocache(res.start, resource_size(&res));
+ if(!sr2351_fm_base.aonckg_base)
+ {
+ BUG();
+ }
+ SR2351_PRINT("pmu reg base is 0x%x\n", sr2351_fm_base.aonckg_base);
+
+ ret = of_address_to_resource(np, 4, &res);
+ if (ret < 0)
+ {
+ SR2351_PRINT("Can't get the pin reg base!\n");
+ return -EIO;
+ }
+ sr2351_fm_base.pin_base = (unsigned long)ioremap_nocache(res.start, resource_size(&res));
+ if(!sr2351_fm_base.pin_base)
+ {
+ BUG();
+ }
+ SR2351_PRINT("pin reg base is 0x%x\n", sr2351_fm_base.pin_base);
+
+
+ return 0;
+}
+
+static const struct of_device_id of_match_table_fm[] = {
+ { .compatible = "sprd,sprd_fm", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_match_table_fm);
+#endif
+
+static int sr2351_fm_probe(struct platform_device *pdev)
+{
+ int ret = -EINVAL;
+ char *ver_str = SR2351_FM_VERSION;
+
+#ifdef CONFIG_OF
+ struct device_node *np;
+ np = pdev->dev.of_node;
+
+ ret = sr2351_fm_parse_dts(np);
+ if (ret < 0)
+ {
+ SR2351_PRINT("sr2351_fm_parse_dts failed!");
+ return ret;
+ }
+#endif
+
+ SR2351_PRINT("**********************************************");
+ SR2351_PRINT(" SR2351 FM driver ");
+ SR2351_PRINT(" Version: %s", ver_str);
+ SR2351_PRINT(" Build date: %s %s", __DATE__, __TIME__);
+ SR2351_PRINT("**********************************************");
+
+ ret = misc_register(&sr2351_fm_misc_device);
+ if (ret < 0)
+ {
+ SR2351_PRINT("misc_register failed!");
+ return ret;
+ }
+
+ SR2351_PRINT("sr2351_fm_init success.\n");
+
+ return 0;
+}
+
+static int sr2351_fm_remove(struct platform_device *pdev)
+{
+
+ SR2351_PRINT("exit_fm_driver!\n");
+ misc_deregister(&sr2351_fm_misc_device);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int sr2351_fm_suspend(struct platform_device *dev, pm_message_t state)
+{
+ sr2351_fm_enter_sleep();
+ return 0;
+}
+
+static int sr2351_fm_resume(struct platform_device *dev)
+{
+ sr2351_fm_exit_sleep();
+ return 0;
+}
+#else
+#define sr2351_fmt_suspend NULL
+#define sr2351_fm_resume NULL
+#endif
+
+
+static struct platform_driver sr2351_fm_driver = {
+ .driver = {
+ .name = "sprd_fm",
+ .owner = THIS_MODULE,
+ #ifdef CONFIG_OF
+ .of_match_table = of_match_ptr(of_match_table_fm) ,
+ #endif
+ },
+ .probe = sr2351_fm_probe,
+ .remove = sr2351_fm_remove,
+ .suspend = sr2351_fm_suspend,
+ .resume = sr2351_fm_resume,
+};
+
+#ifndef CONFIG_OF
+struct platform_device sr2351_fm_device = {
+ .name = "sprd_fm",
+ .id = -1,
+};
+#endif
+
+int __init init_fm_driver(void)
+{
+ int ret;
+#ifndef CONFIG_OF
+ ret = platform_device_register(&sr2351_fm_device);
+ if (ret)
+ {
+ SR2351_PRINT("sr2351_fm: platform_device_register failed: %d\n", ret);
+ return ret;
+ }
+#endif
+ ret = platform_driver_register(&sr2351_fm_driver);
+ if (ret)
+ {
+ #ifndef CONFIG_OF
+ platform_device_unregister(&sr2351_fm_device);
+ #endif
+ SR2351_PRINT("sr2351_fm: probe failed: %d\n", ret);
+ }
+ SR2351_PRINT("sr2351_fm: probe sucess: %d\n", ret);
+
+ return ret;
+}
+
+void __exit exit_fm_driver(void)
+{
+ platform_driver_unregister(&sr2351_fm_driver);
+}
+
+module_init(init_fm_driver);
+module_exit(exit_fm_driver);
+
+MODULE_DESCRIPTION("SR2351 FM radio driver");
+MODULE_AUTHOR("Spreadtrum Inc.");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(SR2351_FM_VERSION);
+
diff --git a/drivers/misc/fm_2351/sr2351_fm_ctrl.h b/drivers/misc/fm_2351/sr2351_fm_ctrl.h
new file mode 100644
index 00000000..40587790
--- /dev/null
+++ b/drivers/misc/fm_2351/sr2351_fm_ctrl.h
@@ -0,0 +1,274 @@
+#ifndef SR2351_FM_CTRL_H__
+#define SR2351_FM_CTRL_H_
+
+#include <soc/sprd/hardware.h>
+#include <linux/sprd_2351.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/sci.h>
+
+#define SR2351_FM_DEV_NAME "Trout_FM"
+
+
+extern int sr2351_fm_en(void);
+extern int sr2351_fm_dis(void);
+extern int sr2351_fm_get_status(int *status);
+extern int sr2351_fm_get_frequency(u32 *);
+extern void sr2351_fm_enter_sleep(void);
+extern void sr2351_fm_exit_sleep(void);
+extern void sr2351_fm_mute(void);
+extern void sr2351_fm_unmute(void);
+extern int sr2351_fm_init(void);
+extern int sr2351_fm_deinit(void);
+extern int sr2351_fm_set_tune(u32 freq);
+extern int sr2351_fm_seek(u32 frequency, u32 seek_dir, u32 time_out, u32 *freq_found);
+extern int sr2351_fm_stop_seek(void);
+extern int sr2351_fm_get_rssi(u32 *);
+extern int sr2351_fm_set_band(u32 min_fm_freq,u32 max_fm_freq);
+extern int sr2351_fm_check_status(u8 *fm_status);
+
+
+/*FM Module Start*/
+/* seek direction */
+#define SR2351_SEEK_DIR_UP 0
+#define SR2351_SEEK_DIR_DOWN 1
+#define SR2351_SEEK_TIMEOUT 1
+
+/** The following define the IOCTL command values via the ioctl macros */
+#define FM_IOCTL_BASE 'R'
+#define FM_IOCTL_ENABLE _IOW(FM_IOCTL_BASE, 0, int)
+#define FM_IOCTL_GET_ENABLE _IOW(FM_IOCTL_BASE, 1, int)
+#define FM_IOCTL_SET_TUNE _IOW(FM_IOCTL_BASE, 2, int)
+#define FM_IOCTL_GET_FREQ _IOW(FM_IOCTL_BASE, 3, int)
+#define FM_IOCTL_SEARCH _IOW(FM_IOCTL_BASE, 4, int[4])
+#define FM_IOCTL_STOP_SEARCH _IOW(FM_IOCTL_BASE, 5, int)
+#define FM_IOCTL_MUTE _IOW(FM_IOCTL_BASE, 6, int)
+#define FM_IOCTL_SET_VOLUME _IOW(FM_IOCTL_BASE, 7, int)
+#define FM_IOCTL_GET_VOLUME _IOW(FM_IOCTL_BASE, 8, int)
+#define FM_IOCTL_CONFIG _IOW(FM_IOCTL_BASE, 9, int)
+#define FM_IOCTL_GET_RSSI _IOW(FM_IOCTL_BASE, 10, int)
+#define FM_IOCTL_SET_BAND _IOW(FM_IOCTL_BASE, 11, int[2])
+#define FM_IOCTL_CHECK_STATUS _IOW(FM_IOCTL_BASE, 12,int)
+
+
+
+#ifdef CONFIG_FM_SEEK_STEP_50KHZ
+#define MAX_FM_FREQ 10800
+#define MIN_FM_FREQ 8750
+#else
+#define MAX_FM_FREQ 1080
+#define MIN_FM_FREQ 875
+#endif
+
+#define FM_CTL_STI_MODE_NORMAL 0x0
+#define FM_CTL_STI_MODE_SEEK 0x1
+#define FM_CTL_STI_MODE_TUNE 0x2
+
+#ifndef CONFIG_OF
+#define SHARK_FM_REG_BASE SPRD_FM_BASE
+#define SHARK_APB_BASE_ADDR SPRD_AONAPB_BASE
+#define SHARK_PMU_BASE_ADDR SPRD_PMU_BASE
+#define SHARK_AON_CLK_BASE_ADDR SPRD_AONCKG_BASE
+#define SHARK_PIN_BASE SPRD_PIN_BASE
+#else
+extern unsigned long rf2351_fm_get_fm_base(void);
+extern unsigned long rf2351_fm_get_apb_base(void);
+extern unsigned long rf2351_fm_get_pmu_base(void);
+extern unsigned long rf2351_fm_get_aonckg_base(void);
+extern unsigned long rf2351_fm_get_pin_base(void);
+
+#define SHARK_FM_REG_BASE rf2351_fm_get_fm_base()
+#define SHARK_APB_BASE_ADDR rf2351_fm_get_apb_base()
+#define SHARK_PMU_BASE_ADDR rf2351_fm_get_pmu_base()
+#define SHARK_AON_CLK_BASE_ADDR rf2351_fm_get_aonckg_base()
+#define SHARK_PIN_BASE rf2351_fm_get_pin_base()
+#endif
+
+#define FM_REG_FM_CTRL (SHARK_FM_REG_BASE + 0x0000)
+#define FM_REG_FM_EN (SHARK_FM_REG_BASE + 0x0004)
+#define FM_REG_RF_INIT_GAIN (SHARK_FM_REG_BASE + 0x0008)
+#define FM_REG_CHAN (SHARK_FM_REG_BASE + 0x000C)
+#define FM_REG_AGC_TBL_CLK (SHARK_FM_REG_BASE + 0x0010)
+#define FM_REG_SEEK_LOOP (SHARK_FM_REG_BASE + 0x0014)
+#define FM_REG_FMCTL_STI (SHARK_FM_REG_BASE + 0x0018)
+#define FM_REG_BAND_LMT (SHARK_FM_REG_BASE + 0x001C)
+#define FM_REG_BAND_SPACE (SHARK_FM_REG_BASE + 0x0020)
+#define FM_REG_RF_CTL (SHARK_FM_REG_BASE + 0x0024)
+#define FM_REG_RF_CTL1 (SHARK_FM_REG_BASE + 0x0028)
+#define FM_REG_INT_EN (SHARK_FM_REG_BASE + 0x0030)
+#define FM_REG_INT_CLR (SHARK_FM_REG_BASE + 0x0034)
+#define FM_REG_INT_STS (SHARK_FM_REG_BASE + 0x0038)
+#define FM_REG_ADC_INFCTRL (SHARK_FM_REG_BASE + 0x003C)
+#define FM_REG_SEEK_CH_TH (SHARK_FM_REG_BASE + 0x0040)
+#define FM_REG_AGC_TBL_RFRSSI0 (SHARK_FM_REG_BASE + 0x0044)
+#define FM_REG_AGC_TBL_RFRSSI1 (SHARK_FM_REG_BASE + 0x0048)
+#define FM_REG_AGC_TBL_WBRSSI (SHARK_FM_REG_BASE + 0x004C)
+#define FM_REG_AGC_IDX_TH (SHARK_FM_REG_BASE + 0x0050)
+#define FM_REG_AGC_RSSI_TH (SHARK_FM_REG_BASE + 0x0054)
+#define FM_REG_SEEK_ADPS (SHARK_FM_REG_BASE + 0x0058)
+#define FM_REG_STER_PWR (SHARK_FM_REG_BASE + 0x005C)
+#define FM_REG_AGC_CTRL (SHARK_FM_REG_BASE + 0x0060)
+#define FM_REG_AGC_ITV_TH (SHARK_FM_REG_BASE + 0x0064)
+#define FM_REG_AGC_ADPS0 (SHARK_FM_REG_BASE + 0x0068)
+#define FM_REG_AGC_ADPS1 (SHARK_FM_REG_BASE + 0x006C)
+#define FM_REG_PDP_TH (SHARK_FM_REG_BASE + 0x0070)
+#define FM_REG_PDP_DEV (SHARK_FM_REG_BASE + 0x0074)
+#define FM_REG_ADP_ST_CONF (SHARK_FM_REG_BASE + 0x0078)
+#define FM_REG_ADP_LPF_CONF (SHARK_FM_REG_BASE + 0x007C)
+#define FM_REG_DEPHA_SCAL (SHARK_FM_REG_BASE + 0x0080)
+#define FM_REG_HW_MUTE (SHARK_FM_REG_BASE + 0x0084)
+#define FM_REG_SW_MUTE (SHARK_FM_REG_BASE + 0x0088)
+#define FM_REG_UPD_CTRL (SHARK_FM_REG_BASE + 0x008C)
+#define FM_REG_AUD_BLD0 (SHARK_FM_REG_BASE + 0x0090)
+#define FM_REG_AUD_BLD1 (SHARK_FM_REG_BASE + 0x0094)
+#define FM_REG_AGC_HYS (SHARK_FM_REG_BASE + 0x0098)
+#define FM_REG_MONO_PWR (SHARK_FM_REG_BASE + 0x009C)
+#define FM_REG_RF_CFG_DLY (SHARK_FM_REG_BASE + 0x00A0)
+#define FM_REG_AGC_TBL_STS (SHARK_FM_REG_BASE + 0x00A4)
+#define FM_REG_ADP_BIT_SFT (SHARK_FM_REG_BASE + 0x00A8)
+#define FM_REG_SEEK_CNT (SHARK_FM_REG_BASE + 0x00AC)
+#define FM_REG_RSSI_STS (SHARK_FM_REG_BASE + 0x00B0)
+#define FM_REG_CHAN_FREQ_STS (SHARK_FM_REG_BASE + 0x00B8)
+#define FM_REG_FREQ_OFF_STS (SHARK_FM_REG_BASE + 0x00BC)
+#define FM_REG_INPWR_STS (SHARK_FM_REG_BASE + 0x00C0)
+#define FM_REG_RF_RSSI_STS (SHARK_FM_REG_BASE + 0x00C4)
+#define FM_REG_NO_LPF_STS (SHARK_FM_REG_BASE + 0x00C8)
+#define FM_REG_SMUTE_STS (SHARK_FM_REG_BASE + 0x00CC)
+#define FM_REG_WBRSSI_STS (SHARK_FM_REG_BASE + 0x00D0)
+#define FM_REG_AGC_BITS_TBL0 (SHARK_FM_REG_BASE + 0x0100)
+#define FM_REG_AGC_BITS_TBL1 (SHARK_FM_REG_BASE + 0x0104)
+#define FM_REG_AGC_BITS_TBL2 (SHARK_FM_REG_BASE + 0x0108)
+#define FM_REG_AGC_BITS_TBL3 (SHARK_FM_REG_BASE + 0x010C)
+#define FM_REG_AGC_BITS_TBL4 (SHARK_FM_REG_BASE + 0x0110)
+#define FM_REG_AGC_BITS_TBL5 (SHARK_FM_REG_BASE + 0x0114)
+#define FM_REG_AGC_BITS_TBL6 (SHARK_FM_REG_BASE + 0x0118)
+#define FM_REG_AGC_BITS_TBL7 (SHARK_FM_REG_BASE + 0x011C)
+#define FM_REG_AGC_BITS_TBL8 (SHARK_FM_REG_BASE + 0x0120)
+#define FM_REG_AGC_BITS_TBL9 (SHARK_FM_REG_BASE + 0x0124)
+#define FM_REG_AGC_BITS_TBL10 (SHARK_FM_REG_BASE + 0x0128)
+#define FM_REG_AGC_BITS_TBL11 (SHARK_FM_REG_BASE + 0x012C)
+#define FM_REG_AGC_BITS_TBL12 (SHARK_FM_REG_BASE + 0x0130)
+#define FM_REG_AGC_BITS_TBL13 (SHARK_FM_REG_BASE + 0x0134)
+#define FM_REG_AGC_BITS_TBL14 (SHARK_FM_REG_BASE + 0x0138)
+#define FM_REG_AGC_BITS_TBL15 (SHARK_FM_REG_BASE + 0x013C)
+#define FM_REG_AGC_BITS_TBL16 (SHARK_FM_REG_BASE + 0x0140)
+#define FM_REG_AGC_BITS_TBL17 (SHARK_FM_REG_BASE + 0x0144)
+#define FM_REG_AGC_VAL_TBL0 (SHARK_FM_REG_BASE + 0x0200)
+#define FM_REG_AGC_VAL_TBL1 (SHARK_FM_REG_BASE + 0x0204)
+#define FM_REG_AGC_VAL_TBL2 (SHARK_FM_REG_BASE + 0x0208)
+#define FM_REG_AGC_VAL_TBL3 (SHARK_FM_REG_BASE + 0x020C)
+#define FM_REG_AGC_VAL_TBL4 (SHARK_FM_REG_BASE + 0x0210)
+#define FM_REG_AGC_VAL_TBL5 (SHARK_FM_REG_BASE + 0x0214)
+#define FM_REG_AGC_VAL_TBL6 (SHARK_FM_REG_BASE + 0x0218)
+#define FM_REG_AGC_VAL_TBL7 (SHARK_FM_REG_BASE + 0x021C)
+#define FM_REG_AGC_VAL_TBL8 (SHARK_FM_REG_BASE + 0x0220)
+#define FM_REG_AGC_VAL_TBL9 (SHARK_FM_REG_BASE + 0x0224)
+#define FM_REG_AGC_VAL_TBL10 (SHARK_FM_REG_BASE + 0x0228)
+#define FM_REG_AGC_VAL_TBL11 (SHARK_FM_REG_BASE + 0x022C)
+#define FM_REG_AGC_VAL_TBL12 (SHARK_FM_REG_BASE + 0x0230)
+#define FM_REG_AGC_VAL_TBL13 (SHARK_FM_REG_BASE + 0x0234)
+#define FM_REG_AGC_VAL_TBL14 (SHARK_FM_REG_BASE + 0x0238)
+#define FM_REG_AGC_VAL_TBL15 (SHARK_FM_REG_BASE + 0x023C)
+#define FM_REG_AGC_VAL_TBL16 (SHARK_FM_REG_BASE + 0x0240)
+#define FM_REG_AGC_VAL_TBL17 (SHARK_FM_REG_BASE + 0x0244)
+#define FM_REG_AGC_BOND_TBL0 (SHARK_FM_REG_BASE + 0x0300)
+#define FM_REG_AGC_BOND_TBL1 (SHARK_FM_REG_BASE + 0x0304)
+#define FM_REG_AGC_BOND_TBL2 (SHARK_FM_REG_BASE + 0x0308)
+#define FM_REG_AGC_BOND_TBL3 (SHARK_FM_REG_BASE + 0x030C)
+#define FM_REG_AGC_BOND_TBL4 (SHARK_FM_REG_BASE + 0x0310)
+#define FM_REG_AGC_BOND_TBL5 (SHARK_FM_REG_BASE + 0x0314)
+#define FM_REG_AGC_BOND_TBL6 (SHARK_FM_REG_BASE + 0x0318)
+#define FM_REG_AGC_BOND_TBL7 (SHARK_FM_REG_BASE + 0x031C)
+#define FM_REG_AGC_BOND_TBL8 (SHARK_FM_REG_BASE + 0x0320)
+#define FM_REG_AGC_BOND_TBL9 (SHARK_FM_REG_BASE + 0x0324)
+
+#define FM_REG_SPUR_FREQ0 (SHARK_FM_REG_BASE + 0x340) /* new reg*/
+#define FM_REG_SPUR_FREQ1 (SHARK_FM_REG_BASE + 0X344) /* new reg*/
+
+#define FM_REG_SPUR_RM_CTL (SHARK_FM_REG_BASE + 0X348)
+#define FM_SPUR_RM_CTL_DEFAULT_VALUE (0X88FFFF)
+#define FM_SPUR_RM_CTL_SET_VALUE (0X551F1F)
+#define FM_REG_SPUR_DC_RM_CTL (SHARK_FM_REG_BASE + 0X34C)
+#define FM_SPUR_DC_RM_CTL_DEFAULT_VALUE (0X10FFFF)
+#define FM_SPUR_DC_RM_CTL_SET_VALUE (0XA03FF)
+
+
+#define FM_REG_AGC_DB_TBL_BEGIN (SHARK_FM_REG_BASE + 0x0500)
+#define FM_REG_AGC_DB_TBL_END (SHARK_FM_REG_BASE + 0x05A0)
+#define FM_REG_AGC_DB_TBL_CNT \
+ ((0x5a0-0x500)/4 + 1)
+
+#define FM_REG_SPI_CTL (SHARK_FM_REG_BASE + 0x0810)
+#define FM_REG_SPI_WD0 (SHARK_FM_REG_BASE + 0x0814)
+#define FM_REG_SPI_WD1 (SHARK_FM_REG_BASE + 0x0818)
+#define FM_REG_SPI_RD (SHARK_FM_REG_BASE + 0x081C)
+#define FM_REG_SPI_FIFO_STS (SHARK_FM_REG_BASE + 0x0820)
+
+#define SHARK_APB_EB0 REG_AON_APB_APB_EB0
+#define SHARK_APB_EB1 REG_AON_APB_APB_EB1
+#define SHARK_APB_RST0 REG_AON_APB_APB_RST0
+#define SHARK_APB_EB0_SET (REG_AON_APB_APB_EB0+0x1000)
+
+#define SHARK_PMU_SLEEP_CTRL REG_PMU_APB_SLEEP_CTRL
+
+#define SHARK_PMU_APB_MEM_PD_CFG0 REG_PMU_APB_MEM_PD_CFG0
+
+#if defined(CONFIG_ARCH_SCX30G)
+#define SHARK_MSPI_CLK_SWITCH (SHARK_AON_CLK_BASE_ADDR + 0x0054)
+#elif defined(CONFIG_ARCH_SCX15) || defined(CONFIG_ARCH_SCX35)
+#define SHARK_MSPI_CLK_SWITCH (SHARK_AON_CLK_BASE_ADDR + 0x0050)
+#endif
+
+#define SHARK_PMU_APB_PD_AP_SYS_CFG REG_PMU_APB_PD_AP_SYS_CFG
+
+#define INT_NUM_FM_test 27
+
+#define SHARK_CHIP_BD 1
+
+#define SHARK_PIN_U0TXD (SHARK_PIN_BASE + 0x0048)
+#define SHARK_PIN_U0RXD (SHARK_PIN_BASE + 0x004c)
+#define SHARK_PIN_U0CTS (SHARK_PIN_BASE + 0x0050)
+
+
+struct shark_fm_info_t {
+ int int_happen;
+ int seek_success;
+ u32 freq_set;
+ u32 freq_seek;
+ u32 freq_offset;
+ u32 rssi;
+ u32 rf_rssi;
+ u32 seek_cnt;
+ u32 inpwr_sts;
+ u32 fm_sts;
+ u32 agc_sts;
+ /*void (*irq_handler)(u32);*/
+};
+
+
+struct shark_reg_cfg_t {
+ unsigned int reg_addr;
+ unsigned int reg_data;
+};
+
+/*FM Module End*/
+
+/*2351 RF Start*/
+
+#define FM_SR2351_ADC_CLK 0x06F
+#define FM_SR2351_DCOC_CAL_TIMER 0x471
+#define FM_SR2351_RC_TUNER 0x477
+#define FM_SR2351_RX_ADC_CLK 0x478
+#define FM_SR2351_OVERLOAD_DET 0x431
+#define FM_SR2351_RX_GAIN 0x404
+#define FM_SR2351_MODE 0x400
+#define FM_SR2351_FREQ 0x402
+
+/*2351 RF End*/
+
+#define SR2351_PRINT(format, arg...) \
+ do { \
+ printk("sr2351_fm %s-%d -- "format"\n", \
+ __func__, __LINE__, ## arg); \
+ } while (0)
+
+#endif
diff --git a/drivers/misc/fm_2351/sr2351_fm_rf.c b/drivers/misc/fm_2351/sr2351_fm_rf.c
new file mode 100644
index 00000000..c7464568
--- /dev/null
+++ b/drivers/misc/fm_2351/sr2351_fm_rf.c
@@ -0,0 +1,822 @@
+#include <linux/miscdevice.h>
+#include <linux/sysfs.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/ioctl.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <asm/io.h>
+#ifdef CONFIG_ARCH_SCX20
+#include <linux/sprd_2351.h>
+#endif
+
+#include "sr2351_fm_ctrl.h"
+
+static u32 LOCAL_MIN_FM_FREQ = MIN_FM_FREQ;
+static u32 LOCAL_MAX_FM_FREQ = MAX_FM_FREQ;
+
+struct sprd_2351_interface *fm_rf_ops = NULL;
+
+struct shark_fm_info_t shark_fm_info = {
+ 1, /* int_happen;*/
+ 1, /* seek_success;*/
+ 0, /* freq_set;*/
+ 0, /* freq_seek;*/
+ 0, /* freq_offset;*/
+ 0, /* rssi;*/
+ 0, /* rf_rssi;*/
+ 0, /* seek_cnt;*/
+ 0, /* inpwr_sts;*/
+ 0, /* fm_sts;*/
+ 0, /* agc_sts*/
+ /*shark_fm_irq_handler, (*irq_handler)(u32);*/
+};
+
+typedef struct {
+ uint32_t ver;
+ uint8_t cap;
+}rom_callback_func_t;
+
+unsigned int get_shark_chip_id(void)
+{
+ unsigned int code_addr = 0;
+ unsigned int header_phy_addr = 0;
+ unsigned int header_addr = 0;
+ unsigned int header_offset = 0;
+ unsigned int chip_ver=0;
+
+ code_addr =(volatile unsigned int) ioremap((unsigned int)0xFFFF0000,0x4000);
+ header_phy_addr = (rom_callback_func_t *)(*((unsigned int*)(code_addr +0x20)));
+ header_offset=header_phy_addr-0xffff0000;
+ header_addr=header_offset+code_addr;
+ chip_ver=*(unsigned int *)(header_addr);
+
+ iounmap(code_addr);
+
+ return chip_ver;
+}
+
+static void read_fm_reg(u32 reg_addr, u32 *reg_data)
+{
+ *reg_data = sci_glb_read(reg_addr,-1UL);
+}
+
+static unsigned int write_fm_reg(u32 reg_addr, u32 val)
+{
+ sci_glb_write(reg_addr, val, -1UL);
+ return 0;
+}
+
+static void fm_register_set(void)
+{
+ write_fm_reg(FM_REG_FM_CTRL , 0x00001011);
+ write_fm_reg(FM_REG_FM_EN , 0x000032EE);
+ write_fm_reg(FM_REG_CHAN , (899-1)*2);
+ write_fm_reg(FM_REG_AGC_TBL_CLK, 0x00000000);
+#ifdef CONFIG_FM_SEEK_STEP_50KHZ
+ write_fm_reg(FM_REG_SEEK_LOOP , (LOCAL_MAX_FM_FREQ - LOCAL_MIN_FM_FREQ)/10));
+#else
+ write_fm_reg(FM_REG_SEEK_LOOP , (LOCAL_MAX_FM_FREQ - LOCAL_MIN_FM_FREQ));
+#endif
+ write_fm_reg(FM_REG_FMCTL_STI , 0x0);
+#ifdef CONFIG_FM_SEEK_STEP_50KHZ
+ write_fm_reg(FM_REG_BAND_LMT , ((((LOCAL_MAX_FM_FREQ/10) - 1) * 2)<<16) | (((LOCAL_MIN_FM_FREQ/10)-1)*2));
+#else
+ write_fm_reg(FM_REG_BAND_LMT , (((LOCAL_MAX_FM_FREQ - 1) * 2)<<16) | ((LOCAL_MIN_FM_FREQ-1)*2));
+#endif
+#ifdef CONFIG_FM_SEEK_STEP_50KHZ
+ write_fm_reg(FM_REG_BAND_SPACE , 0x00000001);
+#else
+ write_fm_reg(FM_REG_BAND_SPACE , 0x00000002);
+#endif
+ write_fm_reg(FM_REG_ADC_INFCTRL, 0);
+ write_fm_reg(FM_REG_SEEK_CH_TH , 2000);
+ write_fm_reg(FM_REG_AGC_TBL_RFRSSI0, 0x00020800);
+ write_fm_reg(FM_REG_AGC_TBL_RFRSSI1, (31 << 24) | (5 << 16) | (3000));
+ write_fm_reg(FM_REG_AGC_TBL_WBRSSI , 0x00F60C12);
+ write_fm_reg(FM_REG_AGC_IDX_TH , 0x00020407);
+ write_fm_reg(FM_REG_AGC_RSSI_TH , 0x00F600D8);
+ write_fm_reg(FM_REG_SEEK_ADPS , 0x0CD80000);
+ write_fm_reg(FM_REG_STER_PWR , 0x000001A6);
+ write_fm_reg(FM_REG_MONO_PWR , 0x019C0192);
+ write_fm_reg(FM_REG_AGC_CTRL , 0x09040120);
+ write_fm_reg(FM_REG_AGC_ITV_TH , 0x00FE00EE);
+ write_fm_reg(FM_REG_AGC_ADPS0 , 0xFDF70009);
+ write_fm_reg(FM_REG_AGC_ADPS1 , 0xFEF80003);
+ write_fm_reg(FM_REG_PDP_TH , 400);
+ write_fm_reg(FM_REG_PDP_DEV , 120);
+ write_fm_reg(FM_REG_ADP_ST_CONF , 0x200501BA);
+ write_fm_reg(FM_REG_ADP_LPF_CONF , 2);
+ write_fm_reg(FM_REG_DEPHA_SCAL , 5);
+ write_fm_reg(FM_REG_HW_MUTE , 0);
+ write_fm_reg(FM_REG_SW_MUTE , 0x01A601A1);
+ write_fm_reg(FM_REG_UPD_CTRL , 0x01013356);
+ write_fm_reg(FM_REG_AUD_BLD0 , 0x01AD01A1);
+ write_fm_reg(FM_REG_AUD_BLD1 , 0x81A901A5);
+ write_fm_reg(FM_REG_AGC_HYS , 0x00000202);
+ write_fm_reg(FM_REG_RF_CFG_DLY , 20000);
+ write_fm_reg(FM_REG_RF_INIT_GAIN, 0x0303);
+ write_fm_reg(FM_REG_AGC_BITS_TBL0 , 0x0000);
+ write_fm_reg(FM_REG_AGC_BITS_TBL1 , 0x0001);
+ write_fm_reg(FM_REG_AGC_BITS_TBL2 , 0x0301);
+ write_fm_reg(FM_REG_AGC_BITS_TBL3 , 0x0302);
+ write_fm_reg(FM_REG_AGC_BITS_TBL4 , 0x0303);
+ write_fm_reg(FM_REG_AGC_BITS_TBL5 , 0x0313);
+ write_fm_reg(FM_REG_AGC_BITS_TBL6 , 0x0323);
+ write_fm_reg(FM_REG_AGC_BITS_TBL7 , 0x0333);
+ write_fm_reg(FM_REG_AGC_BITS_TBL8 , 0x0335);
+ write_fm_reg(FM_REG_AGC_BITS_TBL9 , 0x0000);
+ write_fm_reg(FM_REG_AGC_BITS_TBL10 , 0x0001);
+ write_fm_reg(FM_REG_AGC_BITS_TBL11 , 0x0301);
+ write_fm_reg(FM_REG_AGC_BITS_TBL12 , 0x0302);
+ write_fm_reg(FM_REG_AGC_BITS_TBL13 , 0x0303);
+ write_fm_reg(FM_REG_AGC_BITS_TBL14 , 0x0313);
+ write_fm_reg(FM_REG_AGC_BITS_TBL15 , 0x0323);
+ write_fm_reg(FM_REG_AGC_BITS_TBL16 , 0x0333);
+ write_fm_reg(FM_REG_AGC_BITS_TBL17 , 0x0335);
+ write_fm_reg(FM_REG_AGC_VAL_TBL0 , 7);
+ write_fm_reg(FM_REG_AGC_VAL_TBL1 , 21);
+ write_fm_reg(FM_REG_AGC_VAL_TBL2 , 29);
+ write_fm_reg(FM_REG_AGC_VAL_TBL3 , 35);
+ write_fm_reg(FM_REG_AGC_VAL_TBL4 , 41);
+ write_fm_reg(FM_REG_AGC_VAL_TBL5 , 47);
+ write_fm_reg(FM_REG_AGC_VAL_TBL6 , 53);
+ write_fm_reg(FM_REG_AGC_VAL_TBL7 , 59);
+ write_fm_reg(FM_REG_AGC_VAL_TBL8 , 71);
+ write_fm_reg(FM_REG_AGC_BOND_TBL0 , -120UL);
+ write_fm_reg(FM_REG_AGC_BOND_TBL1 , -89UL);
+ write_fm_reg(FM_REG_AGC_BOND_TBL2 , -83UL);
+ write_fm_reg(FM_REG_AGC_BOND_TBL3 , -77UL);
+ write_fm_reg(FM_REG_AGC_BOND_TBL4 , -41UL);
+ write_fm_reg(FM_REG_AGC_BOND_TBL5 , -35UL);
+ write_fm_reg(FM_REG_AGC_BOND_TBL6 , -29UL);
+ write_fm_reg(FM_REG_AGC_BOND_TBL7 , -21UL);
+ write_fm_reg(FM_REG_AGC_BOND_TBL8 , -13UL);
+ write_fm_reg(FM_REG_AGC_BOND_TBL9 , 10);
+ write_fm_reg(FM_REG_SPUR_FREQ0 , 0x0000081e);
+}
+
+void shark_fm_seek_up(void)
+{
+#ifdef CONFIG_FM_SEEK_STEP_50KHZ
+ write_fm_reg(FM_REG_CHAN, shark_fm_info.freq_seek+1);
+#else
+ write_fm_reg(FM_REG_CHAN, shark_fm_info.freq_seek+2);
+#endif
+ sci_glb_set(FM_REG_FM_CTRL, BIT_19);
+}
+
+void shark_fm_seek_down(void)
+{
+#ifdef CONFIG_FM_SEEK_STEP_50KHZ
+ write_fm_reg(FM_REG_CHAN, shark_fm_info.freq_seek-1);
+#else
+ write_fm_reg(FM_REG_CHAN, shark_fm_info.freq_seek-2);
+#endif
+ sci_glb_clr(FM_REG_FM_CTRL, BIT_19);
+}
+
+
+
+irqreturn_t fm_interrupt(int irq, void *dev_id)
+{
+ SR2351_PRINT("interrupt bingo~~~~~~~~~~~~~\n");
+
+ return IRQ_HANDLED;
+}
+
+
+void sr2351_fm_config_xtl(void)
+{
+ sci_glb_set(SHARK_APB_EB0_SET, BIT_20);
+ sci_glb_set(SHARK_PMU_SLEEP_CTRL, BIT_8);
+}
+
+
+int sr2351_fm_en(void)
+{
+#if defined(CONFIG_ARCH_SCX30G)
+
+ //sci_glb_clr(SHARK_PMU_APB_PD_AP_SYS_CFG,BIT_24);
+ sci_glb_clr(SHARK_PIN_U0TXD,BIT_0|BIT_1);
+ sci_glb_set(SHARK_PIN_U0TXD,BIT_0);
+ sci_glb_clr(SHARK_PIN_U0RXD,BIT_0|BIT_1);
+ sci_glb_set(SHARK_PIN_U0RXD,BIT_0);
+ sci_glb_clr(SHARK_PIN_U0CTS,BIT_0|BIT_1);
+ sci_glb_set(SHARK_PIN_U0CTS,BIT_0);
+
+#endif
+ sci_glb_set(FM_REG_FM_EN, BIT_31);
+
+ return 0;
+}
+
+
+int sr2351_fm_dis(void)
+{
+#if defined(CONFIG_ARCH_SCX30G)
+
+ //sci_glb_set(SHARK_PMU_APB_PD_AP_SYS_CFG,BIT_24);
+
+ sci_glb_clr(SHARK_PIN_U0TXD,BIT_0|BIT_1);
+ sci_glb_set(SHARK_PIN_U0TXD,BIT_1);
+ sci_glb_clr(SHARK_PIN_U0RXD,BIT_0|BIT_1);
+ sci_glb_set(SHARK_PIN_U0RXD,BIT_1);
+ sci_glb_clr(SHARK_PIN_U0CTS,BIT_0|BIT_1);
+ sci_glb_set(SHARK_PIN_U0CTS,BIT_1);
+#endif
+ sci_glb_clr(FM_REG_FM_EN, BIT_31);
+
+ return 0;
+}
+
+int sr2351_fm_get_status(int *status)
+{
+ u32 reg_data = 0;
+ read_fm_reg(FM_REG_FM_EN, &reg_data);
+ *status = reg_data >> 31;
+ return 0;
+}
+
+void sr2351_fm_enter_sleep(void)
+{
+ unsigned int chip_ver=0;
+
+ #ifdef CONFIG_ARCH_SCX30G
+ sci_glb_clr(SHARK_PMU_APB_MEM_PD_CFG0,BIT_5|BIT_4|BIT_3|BIT_2|BIT_1|BIT_0);
+ #endif
+
+ if(fm_rf_ops != NULL)
+ {
+ #if defined(CONFIG_ARCH_SCX15) || defined(CONFIG_ARCH_SCX30G)
+
+ /*Disable the RSSI AGC*/
+ sci_glb_clr(FM_REG_FM_EN, BIT_2 | BIT_3);
+ udelay(5);
+
+ /*Switch the mspi clock*/
+ #if defined(CONFIG_ARCH_SCX30G)
+ printk("2351 fm enter sleep switch clock\n");
+ sci_glb_clr(SHARK_MSPI_CLK_SWITCH, BIT_0);
+ #elif defined(CONFIG_ARCH_SCX15)
+ sci_glb_set(SHARK_MSPI_CLK_SWITCH, BIT_0 | BIT_1);
+ #endif
+
+ /*Enable the RSSI AGC*/
+ sci_glb_set(FM_REG_FM_EN, BIT_2 | BIT_3);
+
+ #elif defined(CONFIG_ARCH_SCX35)
+
+ chip_ver = get_shark_chip_id();
+ //TROUT_PRINT("chip_ver in trout_fm_enter_sleep : %d\r\n", chip_ver);
+ /*
+ * BD version is the latest version for shark chip .
+ * In this version, hardware fix the FM sleep bug about the Sensitivity.
+ * So we need modify the sleep action based on the shark chip version.
+ * The chip id of BD/BC version is 1 ,and the one of the old version is 0,
+ * such as BA/BB
+ */
+ if(chip_ver == 0)
+ {
+ sci_glb_clr(FM_REG_FM_EN,BIT_2|BIT_3);
+
+ } else if (SHARK_CHIP_BD == chip_ver) {
+ /*Disable the RSSI AGC*/
+ sci_glb_clr(FM_REG_FM_EN, BIT_2 | BIT_3);
+ udelay(5);
+
+ /*Switch the mspi clock*/
+ sci_glb_set(SHARK_MSPI_CLK_SWITCH, BIT_0 | BIT_1);
+
+ /*Enable the RSSI AGC*/
+ sci_glb_set(FM_REG_FM_EN, BIT_2 | BIT_3);
+ }
+
+
+ #endif
+
+ fm_rf_ops->write_reg(FM_SR2351_RX_GAIN, 0x0313);
+ }
+}
+
+void sr2351_fm_exit_sleep(void)
+{
+ unsigned int chip_ver=0;
+
+ if(fm_rf_ops != NULL)
+ {
+ #if defined(CONFIG_ARCH_SCX15) || defined(CONFIG_ARCH_SCX30G)
+ /*Disable the RSSI AGC*/
+ sci_glb_clr(FM_REG_FM_EN, BIT_2 | BIT_3);
+ udelay(5);
+
+ /*Switch the mspi clock*/
+ #if defined(CONFIG_ARCH_SCX30G)
+ printk("2351 fm exit sleep switch clock\n");
+ sci_glb_set(SHARK_MSPI_CLK_SWITCH, BIT_0);
+ #elif defined(CONFIG_ARCH_SCX15)
+ sci_glb_clr(SHARK_MSPI_CLK_SWITCH, BIT_0 | BIT_1);
+ #endif
+
+ /*Enable the RSSI AGC*/
+ sci_glb_set(FM_REG_FM_EN, BIT_2 | BIT_3);
+
+ #elif defined(CONFIG_ARCH_SCX35)
+
+ chip_ver = get_shark_chip_id();
+ //TROUT_PRINT("chip_ver in trout_fm_exit_sleep : %d\r\n", chip_ver);
+ /*
+ * BD version is the latest version for shark chip .
+ * In this version, hardware fix the FM sleep bug about the Sensitivity.
+ * So we need modify the sleep action based on the shark chip version.
+ * The chip id of BD/BC version is 1 ,and the one of the old version is 0,
+ * such as BA/BB
+ */
+ if(chip_ver == 0)
+ {
+ sci_glb_set(FM_REG_FM_EN, BIT_2|BIT_3);
+
+ } else if (SHARK_CHIP_BD == chip_ver) {
+ /*Disable the RSSI AGC*/
+ sci_glb_clr(FM_REG_FM_EN, BIT_2 | BIT_3);
+ udelay(5);
+
+ /*Switch the mspi clock*/
+ sci_glb_clr(SHARK_MSPI_CLK_SWITCH, BIT_0 | BIT_1);
+
+ /*Enable the RSSI AGC*/
+ sci_glb_set(FM_REG_FM_EN, BIT_2 | BIT_3);
+ }
+
+ #endif
+ }
+
+ #ifdef CONFIG_ARCH_SCX30G
+ sci_glb_set(SHARK_PMU_APB_MEM_PD_CFG0,BIT_5|BIT_3|BIT_1);
+ #endif
+}
+
+void sr2351_fm_mute(void)
+{
+ sci_glb_set(FM_REG_HW_MUTE, BIT_0);
+}
+
+void sr2351_fm_unmute(void)
+{
+ sci_glb_clr(FM_REG_HW_MUTE, BIT_0);
+}
+
+
+
+void shark_fm_int_en(void)
+{
+ shark_fm_info.int_happen = 1;
+ write_fm_reg(FM_REG_INT_EN, 0x01);/*enable fm int*/
+
+ enable_irq(INT_NUM_FM_test);
+}
+
+void shark_fm_int_dis(void)
+{
+ shark_fm_info.int_happen = 1;
+ write_fm_reg(FM_REG_INT_EN, 0x0); /*disable fm int*/
+
+}
+
+void shark_fm_int_clr(void)
+{
+ write_fm_reg(FM_REG_INT_CLR, 0x1);
+}
+
+int enable_shark_fm(void)
+{
+ u32 reg_data = 0;
+ read_fm_reg(SHARK_APB_EB0, &reg_data);
+ if (!(reg_data & BIT_1)) {
+ sci_glb_set(SHARK_APB_EB0, BIT_1);
+
+ /*reset fm*/
+ sci_glb_set(SHARK_APB_RST0, BIT_1);
+ msleep(20);
+ sci_glb_clr(SHARK_APB_RST0, BIT_1);
+ }
+
+ return 0;
+}
+
+int shark_write_regs(u32 reg_addr, u32 reg_cnt, u32 *reg_data)
+{
+ u32 i = 0;
+
+ for (i = 0; i < reg_cnt; i++) {
+ if (write_fm_reg(reg_addr+i*4, reg_data[i]) == -1)
+ return -1;
+ }
+ return 0;
+}
+
+int shark_write_reg_cfg(struct shark_reg_cfg_t *reg_cfg, u32 cfg_cnt)
+{
+ u32 i;
+ for (i = 0; i < cfg_cnt; i++) {
+ if (write_fm_reg(reg_cfg[i].reg_addr, reg_cfg[i].reg_data) == -1)
+ return -1;
+ }
+ return 0;
+}
+
+int shark_fm_reg_cfg(void)
+{
+ u32 reg_data;
+ u32 fm_reg_agc_db_tbl[FM_REG_AGC_DB_TBL_CNT] = {
+ 15848, 12589, 10000, 7943, 6309,
+ 5011, 3981, 3162, 2511, 1995,
+ 1584, 1258, 1000, 794, 630,
+ 501, 398, 316, 251, 199,
+ 158, 125, 100, 79, 63,
+ 50, 39, 31, 25, 19,
+ 15, 12, 10, 7, 6,
+ 5, 3, 3, 2, 1,
+ 1
+ };
+#if 0
+ if (shark_write_reg_cfg(fm_reg_init_des, \
+ ARRAY_SIZE(fm_reg_init_des)) == -1) {
+ /*return FALSE;*/
+ }
+#else
+ fm_register_set();
+#endif
+ if (write_fm_reg(FM_REG_AGC_TBL_CLK, 0x00000001) == -1)
+ return -1;
+ while (1) {
+ msleep(20);
+ read_fm_reg(FM_REG_AGC_TBL_CLK, &reg_data);
+ if (reg_data>>17)
+ break;
+ }
+ if (shark_write_regs(FM_REG_AGC_DB_TBL_BEGIN, FM_REG_AGC_DB_TBL_CNT, \
+ fm_reg_agc_db_tbl) == -1)
+ return -1;
+ msleep(100);
+ if (write_fm_reg(FM_REG_AGC_TBL_CLK, 0x00000000) == -1)
+ ;/*return FALSE;*/
+ return 0;
+}
+
+int shark_fm_cfg_rf_reg(void)
+{
+ fm_rf_ops->write_reg(FM_SR2351_DCOC_CAL_TIMER, 0x0FFF);
+ fm_rf_ops->write_reg(FM_SR2351_RC_TUNER, 0xFFFF);
+ fm_rf_ops->write_reg(FM_SR2351_RX_ADC_CLK, 0x001F);
+ fm_rf_ops->write_reg(FM_SR2351_RX_ADC_CLK, 0x009F);
+ fm_rf_ops->write_reg(FM_SR2351_ADC_CLK, 0x0201);
+ fm_rf_ops->write_reg(FM_SR2351_OVERLOAD_DET, 0x8022);
+
+ fm_rf_ops->write_reg(FM_SR2351_ADC_CLK, 0x0601);
+ fm_rf_ops->write_reg(FM_SR2351_RX_GAIN, 0x0335);
+ fm_rf_ops->write_reg(FM_SR2351_MODE, 0x0011);
+ fm_rf_ops->write_reg(FM_SR2351_FREQ, 0x07A6);
+ fm_rf_ops->write_reg(FM_SR2351_RX_GAIN, 0x0335);
+
+ msleep(20);
+
+ return 0;
+}
+
+int sr2351_fm_init(void)
+{
+#ifdef CONFIG_ARCH_SCX20
+ rf2351_vddwpa_ctrl_power_enable(1);
+#endif
+ sprd_get_rf2351_ops(&fm_rf_ops);
+ fm_rf_ops->mspi_enable();
+
+ sr2351_fm_config_xtl();
+
+ if (enable_shark_fm() != 0)
+ return -1;
+ msleep(20);
+
+ shark_fm_int_en();
+
+ if (shark_fm_reg_cfg() == -1)
+ return -1;
+ if (shark_fm_cfg_rf_reg() == -1)
+ return -1;
+
+ write_fm_reg(FM_REG_RF_CTL, ((0x404<<16)|(0x0402)));
+ write_fm_reg(FM_REG_RF_CTL1, ((0x0466<<16)|(0x0043)));
+
+ shark_fm_info.freq_seek = 860*2;
+ /*result = request_irq(INT_NUM_FM_test,
+ fm_interrupt, IRQF_DISABLED, "sr2351_FM", NULL);*/
+ return 0;
+}
+
+void __sr2351_fm_get_status(void)
+{
+ read_fm_reg(FM_REG_SEEK_CNT, &shark_fm_info.seek_cnt);
+ read_fm_reg(FM_REG_CHAN_FREQ_STS, &shark_fm_info.freq_seek);
+ read_fm_reg(FM_REG_FREQ_OFF_STS, &shark_fm_info.freq_offset);
+ read_fm_reg(FM_REG_RF_RSSI_STS, &shark_fm_info.rf_rssi);
+ read_fm_reg(FM_REG_RSSI_STS, &shark_fm_info.rssi);
+ read_fm_reg(FM_REG_INPWR_STS, &shark_fm_info.inpwr_sts);
+ read_fm_reg(FM_REG_WBRSSI_STS, &shark_fm_info.fm_sts);
+ read_fm_reg(FM_REG_AGC_TBL_STS, &shark_fm_info.agc_sts);
+
+}
+
+void __sr2351_fm_show_status(void)
+{
+ SR2351_PRINT("FM_REG_SEEK_CNT : %08x (%d)\r\n", \
+ shark_fm_info.seek_cnt, shark_fm_info.seek_cnt);
+#ifdef CONFIG_FM_SEEK_STEP_50KHZ
+ SR2351_PRINT("FM_REG_CHAN_FREQ_STS: %08x (%d)\r\n", \
+ shark_fm_info.freq_seek, shark_fm_info.freq_seek*5 + 10);
+#else
+ SR2351_PRINT("FM_REG_CHAN_FREQ_STS: %08x (%d)\r\n", \
+ shark_fm_info.freq_seek, ((shark_fm_info.freq_seek >> 1) + 1));
+#endif
+ SR2351_PRINT("FM_REG_FREQ_OFF_STS : %08x (%d)\r\n", \
+ shark_fm_info.freq_offset, shark_fm_info.freq_offset);
+ SR2351_PRINT("FM_REG_RF_RSSI_STS : %08x\r\n", \
+ shark_fm_info.rf_rssi);
+ SR2351_PRINT("FM_REG_INPWR_STS : %08x\r\n", \
+ shark_fm_info.inpwr_sts);
+ SR2351_PRINT("FM_REG_WBRSSI_STS : %08x\r\n", \
+ shark_fm_info.fm_sts);
+ SR2351_PRINT("FM_REG_RSSI_STS : %08x\r\n", \
+ shark_fm_info.rssi);
+ SR2351_PRINT("FM_REG_AGC_TBL_STS : %08x\r\n", \
+ shark_fm_info.agc_sts);
+
+ SR2351_PRINT("fm work:%d, fm iq:%04x\r\n", \
+ (shark_fm_info.fm_sts >> 20) & 1, \
+ (shark_fm_info.fm_sts >> 16) & 0xf);
+ SR2351_PRINT("pilot detect:%d, stero:%d, seek fail:%d, rssi:%08x\r\n", \
+ (shark_fm_info.rssi >> 18) & 0x3, \
+ (shark_fm_info.rssi >> 17) & 0x1, \
+ (shark_fm_info.rssi >> 16) & 0x1, shark_fm_info.rssi & 0xff);
+}
+
+int sr2351_fm_deinit(void)
+{
+ fm_rf_ops->write_reg(FM_SR2351_MODE, 0x0000);
+ fm_rf_ops->write_reg(FM_SR2351_ADC_CLK, 0x201);
+
+ sci_glb_clr(SHARK_PMU_SLEEP_CTRL, BIT_8);
+
+ fm_rf_ops->mspi_disable();
+ sprd_put_rf2351_ops(&fm_rf_ops);
+#ifdef CONFIG_ARCH_SCX20
+ rf2351_vddwpa_ctrl_power_enable(0);
+#endif
+
+ return 0;
+}
+
+int shark_fm_wait_int(int time_out)
+{
+ int ret = 0;
+ ulong jiffies_comp = 0;
+ u8 is_timeout = 1;
+ u32 seek_succes = 0;
+ unsigned int start_msecond = 0;
+ unsigned int end_msecond = 0;
+
+ jiffies_comp = jiffies;
+ start_msecond = jiffies_to_msecs(jiffies);
+ SR2351_PRINT("start_msecond: %u\n", start_msecond);
+ do {
+ msleep(20);
+ read_fm_reg(FM_REG_INT_STS, &seek_succes);
+
+ is_timeout = time_after(jiffies,
+ jiffies_comp +
+ msecs_to_jiffies(time_out));
+
+ if (is_timeout && (seek_succes==0))
+ {
+ SR2351_PRINT("FM search timeout.");
+ sr2351_fm_dis();
+ ret = SR2351_SEEK_TIMEOUT;
+ }
+
+ if (seek_succes)
+ {
+ SR2351_PRINT("FM found frequency.");
+ break;
+ }
+ } while (seek_succes == 0 && is_timeout == 0);
+
+ end_msecond = jiffies_to_msecs(jiffies);
+ SR2351_PRINT("end_msecond: %u\n", end_msecond);
+ SR2351_PRINT("used time: %ums\n", end_msecond - start_msecond);
+
+ __sr2351_fm_get_status();
+ __sr2351_fm_show_status();
+
+ SR2351_PRINT("\n");
+ return ret;
+}
+
+int sr2351_fm_set_tune(u32 freq)
+{
+ u32 reg_data;
+ sr2351_fm_dis();
+ shark_fm_int_dis();
+ shark_fm_int_clr();
+ /*FM_ADC_Tuning_Value_overwrite(chan);*/
+ write_fm_reg(FM_REG_FMCTL_STI, FM_CTL_STI_MODE_TUNE); /* tune mode*/
+#ifdef CONFIG_FM_SEEK_STEP_50KHZ
+ write_fm_reg(FM_REG_CHAN, ((freq-10)/5) & 0xffff);
+#else
+ write_fm_reg(FM_REG_CHAN, ((freq-1)*2) & 0xffff);
+#endif
+ shark_fm_int_en();
+ sr2351_fm_en();
+
+ read_fm_reg(FM_REG_CHAN, &reg_data);
+ SR2351_PRINT("chan reg_data is %d\n", reg_data);
+
+ shark_fm_wait_int(3000);
+
+ /*dump_fm_regs();*/
+
+ shark_fm_int_clr();
+ return 0;
+}
+
+int sr2351_fm_seek(u32 frequency, u32 seek_dir, u32 time_out, u32 *freq_found)
+{
+ int ret = 0;
+
+ SR2351_PRINT("FM seek, freq(%i) dir(%i) timeout(%i).",frequency, seek_dir, time_out);
+
+ if (frequency < LOCAL_MIN_FM_FREQ || frequency > LOCAL_MAX_FM_FREQ)
+ {
+ SR2351_PRINT("out of freq range: %i", frequency);
+ frequency = LOCAL_MIN_FM_FREQ;
+ }
+
+#ifdef CONFIG_FM_SEEK_STEP_50KHZ
+ shark_fm_info.freq_seek = ((frequency-10)/5);
+#else
+ shark_fm_info.freq_seek = ((frequency-1)*2);
+#endif
+
+ seek_dir ^= 0x1;
+ sr2351_fm_dis();
+ shark_fm_int_clr();
+
+ if (seek_dir == 0)
+ shark_fm_seek_up();
+ else
+ shark_fm_seek_down();
+
+ /*Handel False station,change the spuer value when stark seek*/
+ write_fm_reg(FM_REG_SPUR_RM_CTL, FM_SPUR_RM_CTL_SET_VALUE);
+ write_fm_reg(FM_REG_SPUR_DC_RM_CTL, FM_SPUR_DC_RM_CTL_SET_VALUE);
+ write_fm_reg(FM_REG_FMCTL_STI, FM_CTL_STI_MODE_SEEK); /* seek mode*/
+
+ shark_fm_int_en();
+ sr2351_fm_en();
+ ret = shark_fm_wait_int(time_out);
+ if (ret == SR2351_SEEK_TIMEOUT)
+ {
+#ifdef CONFIG_FM_SEEK_STEP_50KHZ
+ if (seek_dir == 0){
+ if ( LOCAL_MIN_FM_FREQ == (shark_fm_info.freq_seek*5 + 10)){
+ shark_fm_info.freq_seek = (LOCAL_MAX_FM_FREQ -10 ) /5;
+ }
+ else{
+ shark_fm_info.freq_seek -= 1;
+ }
+ }
+ else{
+ if ( LOCAL_MAX_FM_FREQ == shark_fm_info.freq_seek*5 + 10){
+ shark_fm_info.freq_seek = (LOCAL_MIN_FM_FREQ -10 ) /5;
+ }
+ else{
+ shark_fm_info.freq_seek += 1;
+ }
+ }
+ SR2351_PRINT("seek failed!");
+ SR2351_PRINT("freq=%d",shark_fm_info.freq_seek*5 + 10);
+
+#else
+ if (seek_dir == 0)
+ {
+ if ( LOCAL_MIN_FM_FREQ == ((shark_fm_info.freq_seek >> 1) + 1))
+ {
+ shark_fm_info.freq_seek = (LOCAL_MAX_FM_FREQ -1 ) * 2;
+ }
+ else
+ {
+ shark_fm_info.freq_seek -= 2;
+ }
+ }
+ else
+ {
+ if ( LOCAL_MAX_FM_FREQ == ((shark_fm_info.freq_seek >> 1) + 1))
+ {
+ shark_fm_info.freq_seek = (LOCAL_MIN_FM_FREQ -1 ) * 2;
+ }
+ else
+ {
+ shark_fm_info.freq_seek += 2;
+ }
+ }
+ SR2351_PRINT("seek time out!");
+ SR2351_PRINT("freq=%d",((shark_fm_info.freq_seek >> 1) + 1));
+#endif
+ }
+
+ /*dump_fm_regs();*/
+#ifdef CONFIG_FM_SEEK_STEP_50KHZ
+ *freq_found = shark_fm_info.freq_seek*5 + 10;
+#else
+ *freq_found = (shark_fm_info.freq_seek >> 1) + 1;
+#endif
+
+ shark_fm_int_clr();
+
+ /*Handel False station spuer,restore the default value when stop seek*/
+ write_fm_reg(FM_REG_SPUR_RM_CTL, FM_SPUR_RM_CTL_DEFAULT_VALUE);
+ write_fm_reg(FM_REG_SPUR_DC_RM_CTL, FM_SPUR_DC_RM_CTL_DEFAULT_VALUE);
+
+ return ret;
+}
+
+int sr2351_fm_get_frequency(u32 *freq)
+{
+#ifdef CONFIG_FM_SEEK_STEP_50KHZ
+ *freq = shark_fm_info.freq_seek*5 + 10;
+#else
+ *freq = ((shark_fm_info.freq_seek >> 1) + 1);
+#endif
+ return 0;
+}
+
+int sr2351_fm_stop_seek(void)
+{
+ u32 reg_value = 0x0;
+
+ SR2351_PRINT("FM stop seek.");
+
+ /* clear seek enable bit of seek register. */
+ read_fm_reg(FM_REG_FMCTL_STI, &reg_value);
+ if (reg_value & FM_CTL_STI_MODE_SEEK)
+ {
+ reg_value |= FM_CTL_STI_MODE_NORMAL;
+ write_fm_reg(FM_REG_FMCTL_STI, reg_value);
+ }
+
+ return 0;
+}
+
+int sr2351_fm_get_rssi(u32 *rssi)
+{
+ u32 reg_data;
+
+ read_fm_reg(FM_REG_INPWR_STS, &reg_data);
+ *rssi = reg_data;
+ SR2351_PRINT("rssi value in sr2351_fm_get_rssi() is %08x\r\n",*rssi);
+
+ return 0;
+}
+
+int sr2351_fm_set_band(u32 min_fm_freq,u32 max_fm_freq)
+{
+ LOCAL_MIN_FM_FREQ = min_fm_freq;
+ LOCAL_MAX_FM_FREQ = max_fm_freq;
+
+ return 0;
+}
+
+int sr2351_fm_check_status(u8 *fm_status)
+{
+ u32 reg_data = 0;
+ read_fm_reg(FM_REG_FM_EN, &reg_data);
+ if(0X1 == (reg_data >> 31))
+ {
+ *fm_status = 0x00;
+
+ }else if(0X0 == (reg_data >> 31))
+ {
+
+ *fm_status = 0x01;
+
+ }else
+ {
+ *fm_status = 0x02;
+ }
+ return 0;
+}
+