From 2466226cb256e9de8ed6aa44e5d4fc1d8f8e6968 Mon Sep 17 00:00:00 2001 From: Andrey Filippov Date: Sat, 14 Dec 2013 00:15:16 +0000 Subject: [PATCH] Implemented spread spectrum control --- drivers/misc/si5338.c | 563 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 545 insertions(+), 18 deletions(-) diff --git a/drivers/misc/si5338.c b/drivers/misc/si5338.c index e37ccce..ff60a26 100644 --- a/drivers/misc/si5338.c +++ b/drivers/misc/si5338.c @@ -35,8 +35,6 @@ #define SYSFS_READONLY 0444 #define SYSFS_WRITEONLY 0222 - - #define REG5338_PAGE 255 #define REG5338_PAGE_MASK 1 #define REG5338_DEV_CONFIG2 2 @@ -50,6 +48,16 @@ #define INFREQMAX 710000000LL #define INFREQDIV 40000000LL /* divide input frequency if above */ +#define SPREAD_RATE_MIN 31500 /* 31.5 KHz */ +#define SPREAD_RATE_MAX 63000 /* 63 KHz */ +#define SPREAD_AMP_MIN 10 /* 0.1% */ +#define SPREAD_AMP_MAX 500 /* 5.0% */ +#define SPREAD_AMP_DENOM 10000 /* 0.01% amplitude step */ + +#define SPREAD_RATE_DFLT 31500 /* 31.5 KHz */ +#define SPREAD_AMP_DFLT 50 /* 0.5% */ + + #define MSINT_MIN 8 /* not considering 4,6 */ #define MSINT_MAX 567 @@ -228,14 +236,66 @@ #define AWE_SOFT_RESET 0xf602 + +#define AWE_MS0_SSUPP2_07_00 0x11fff +#define AWE_MS0_SSUPP2_14_08 0x1207f #define AWE_MS0_SSUPP3_07_00 0x121ff /* set them to 0 - default==1 */ #define AWE_MS0_SSUPP3_14_08 0x1227f +#define AWE_MS0_SSUPP1_07_00 0x123ff +#define AWE_MS0_SSUPP1_11_08 0x1240f +#define AWE_MS0_SSUDP1_03_00 0x124f0 +#define AWE_MS0_SSUDP1_11_04 0x125ff +#define AWE_MS0_SSDNP2_07_00 0x126ff +#define AWE_MS0_SSDNP2_14_08 0x1277f +#define AWE_MS0_SSDNP3_07_00 0x128ff +#define AWE_MS0_SSDNP3_14_08 0x1297f +#define AWE_MS0_SSDNP1_07_00 0x12aff +#define AWE_MS0_SSDNP1_11_08 0x12b0f + +#define AWE_MS1_SSUPP2_07_00 0x12fff +#define AWE_MS1_SSUPP2_14_08 0x1307f #define AWE_MS1_SSUPP3_07_00 0x131ff #define AWE_MS1_SSUPP3_14_08 0x1327f +#define AWE_MS1_SSUPP1_07_00 0x133ff +#define AWE_MS1_SSUPP1_11_08 0x1340f +#define AWE_MS1_SSUDP1_03_00 0x134f0 +#define AWE_MS1_SSUDP1_11_04 0x135ff +#define AWE_MS1_SSDNP2_07_00 0x136ff +#define AWE_MS1_SSDNP2_14_08 0x1377f +#define AWE_MS1_SSDNP3_07_00 0x138ff +#define AWE_MS1_SSDNP3_14_08 0x1397f +#define AWE_MS1_SSDNP1_07_00 0x13aff +#define AWE_MS1_SSDNP1_11_08 0x13b0f + +#define AWE_MS2_SSUPP2_07_00 0x13fff +#define AWE_MS2_SSUPP2_14_08 0x1407f #define AWE_MS2_SSUPP3_07_00 0x141ff #define AWE_MS2_SSUPP3_14_08 0x1427f +#define AWE_MS2_SSUPP1_07_00 0x143ff +#define AWE_MS2_SSUPP1_11_08 0x1440f +#define AWE_MS2_SSUDP1_03_00 0x144f0 +#define AWE_MS2_SSUDP1_11_04 0x145ff +#define AWE_MS2_SSDNP2_07_00 0x146ff +#define AWE_MS2_SSDNP2_14_08 0x1477f +#define AWE_MS2_SSDNP3_07_00 0x148ff +#define AWE_MS2_SSDNP3_14_08 0x1497f +#define AWE_MS2_SSDNP1_07_00 0x14aff +#define AWE_MS2_SSDNP1_11_08 0x14b0f + +#define AWE_MS3_SSUPP2_07_00 0x14fff +#define AWE_MS3_SSUPP2_14_08 0x1507f #define AWE_MS3_SSUPP3_07_00 0x151ff #define AWE_MS3_SSUPP3_14_08 0x1527f +#define AWE_MS3_SSUPP1_07_00 0x153ff +#define AWE_MS3_SSUPP1_11_08 0x1540f +#define AWE_MS3_SSUDP1_03_00 0x154f0 +#define AWE_MS3_SSUDP1_11_04 0x155ff +#define AWE_MS3_SSDNP2_07_00 0x156ff +#define AWE_MS3_SSDNP2_14_08 0x1577f +#define AWE_MS3_SSDNP3_07_00 0x158ff +#define AWE_MS3_SSDNP3_14_08 0x1597f +#define AWE_MS3_SSDNP1_07_00 0x15aff +#define AWE_MS3_SSDNP1_11_08 0x15b0f @@ -259,6 +319,9 @@ struct si5338_data_t { u64 input_frequency3; u64 input_frequency4; u64 input_frequency56; + u32 ss_on_freq_change; /* 0 - disable SS when frequency is changed, 1 - update SS. +2 reset MS after starting SS*/ + u32 spread_spectrum_rate[4]; /* in Hz */ + u32 spread_spectrum_amp[4]; /* in 0.01% */ // u64 pll_frequency; int reg_addr; /* used for raw register r/w */ int last_page; /* value of last page accessed (bit 0 of register 255) */ @@ -364,11 +427,39 @@ static const char *pll_setup_names[]={"pll_freq_fract", "pll_freq_int", "pll_by_ static const char *out_freq_setup_names[]={ "out0_freq_fract", "out1_freq_fract", "out2_freq_fract", "out3_freq_fract", "out0_freq_int", "out1_freq_int", "out2_freq_int", "out3_freq_int", NULL}; -static u32 awe_msx_ssup3[4][3]= - {{AWE_MS0_SSUPP3_07_00,AWE_MS0_SSUPP3_14_08,0}, - {AWE_MS1_SSUPP3_07_00,AWE_MS1_SSUPP3_14_08,0}, - {AWE_MS2_SSUPP3_07_00,AWE_MS2_SSUPP3_14_08,0}, - {AWE_MS3_SSUPP3_07_00,AWE_MS3_SSUPP3_14_08,0}}; + +static u32 awe_msx_ssup[4][3][3]= + {{{AWE_MS0_SSUPP1_07_00,AWE_MS0_SSUPP1_11_08,0}, + {AWE_MS0_SSUPP2_07_00,AWE_MS0_SSUPP2_14_08,0}, + {AWE_MS0_SSUPP3_07_00,AWE_MS0_SSUPP3_14_08,0}}, + {{AWE_MS1_SSUPP1_07_00,AWE_MS1_SSUPP1_11_08,0}, + {AWE_MS1_SSUPP2_07_00,AWE_MS1_SSUPP2_14_08,0}, + {AWE_MS1_SSUPP3_07_00,AWE_MS1_SSUPP3_14_08,0}}, + {{AWE_MS2_SSUPP1_07_00,AWE_MS2_SSUPP1_11_08,0}, + {AWE_MS2_SSUPP2_07_00,AWE_MS2_SSUPP2_14_08,0}, + {AWE_MS2_SSUPP3_07_00,AWE_MS2_SSUPP3_14_08,0}}, + {{AWE_MS3_SSUPP1_07_00,AWE_MS3_SSUPP1_11_08,0}, + {AWE_MS3_SSUPP2_07_00,AWE_MS3_SSUPP2_14_08,0}, + {AWE_MS3_SSUPP3_07_00,AWE_MS3_SSUPP3_14_08,0}}}; +static u32 awe_msx_ssdn[4][3][3]= + {{{AWE_MS0_SSDNP1_07_00,AWE_MS0_SSDNP1_11_08,0}, + {AWE_MS0_SSDNP2_07_00,AWE_MS0_SSDNP2_14_08,0}, + {AWE_MS0_SSDNP3_07_00,AWE_MS0_SSDNP3_14_08,0}}, + {{AWE_MS1_SSDNP1_07_00,AWE_MS1_SSDNP1_11_08,0}, + {AWE_MS1_SSDNP2_07_00,AWE_MS1_SSDNP2_14_08,0}, + {AWE_MS1_SSDNP3_07_00,AWE_MS1_SSDNP3_14_08,0}}, + {{AWE_MS2_SSDNP1_07_00,AWE_MS2_SSDNP1_11_08,0}, + {AWE_MS2_SSDNP2_07_00,AWE_MS2_SSDNP2_14_08,0}, + {AWE_MS2_SSDNP3_07_00,AWE_MS2_SSDNP3_14_08,0}}, + {{AWE_MS3_SSDNP1_07_00,AWE_MS3_SSDNP1_11_08,0}, + {AWE_MS3_SSDNP2_07_00,AWE_MS3_SSDNP2_14_08,0}, + {AWE_MS3_SSDNP3_07_00,AWE_MS3_SSDNP3_14_08,0}}}; +static u32 awe_msx_ssud[4][3]= + {{AWE_MS0_SSUDP1_03_00,AWE_MS0_SSUDP1_11_04,0}, + {AWE_MS1_SSUDP1_03_00,AWE_MS1_SSUDP1_11_04,0}, + {AWE_MS2_SSUDP1_03_00,AWE_MS2_SSUDP1_11_04,0}, + {AWE_MS3_SSUDP1_03_00,AWE_MS3_SSUDP1_11_04,0}}; + static const u32 awe_rdiv_in[]= {AWE_R0DIV_IN, AWE_R1DIV_IN, AWE_R2DIV_IN, AWE_R3DIV_IN}; static const u32 awe_rdiv_k[]= {AWE_R0DIV, AWE_R1DIV, AWE_R2DIV, AWE_R3DIV}; static const u32 awe_drv_fmt[]= {AWE_DRV0_FMT, AWE_DRV1_FMT, AWE_DRV2_FMT, AWE_DRV3_FMT}; @@ -441,6 +532,15 @@ static ssize_t ms_abc_store(struct device *dev, struct device_attribute *attr, c static ssize_t ms_powerdown_show (struct device *dev, struct device_attribute *attr, char *buf); static ssize_t ms_powerdown_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); +static ssize_t ms_reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); + + +static ssize_t ss_change_freq_mode_show(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t ss_change_freq_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); +static ssize_t ss_vals_show(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t ss_vals_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); +static ssize_t ss_regs_hex_show(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t ss_regs_hex_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); static ssize_t pre_init_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); static ssize_t post_init_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); @@ -503,12 +603,24 @@ static int get_out_frequency_txt(struct device *dev, char *buf, int chn); static ssize_t output_config_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); static int configure_output_driver(struct device *dev, const char * name, int chn); - - -/* -------------------------------------- */ static int remove_common_factor(u64 * num_denom); static int _verify_output_channel(struct i2c_client *client,int chn); +static int get_ss_vals(struct device *dev, char * buf, int chn); +static int get_ss_state(struct i2c_client *client, int chn); +static int set_ss_state(struct i2c_client *client, int state, int chn); +static int get_ss_down_rate(struct i2c_client *client, int chn); +static int get_ss_down_amplitude(struct i2c_client *client, int chn); +static int store_ss_down_parameters(struct i2c_client *client, u32 rate, u32 amp, int chn); +static int set_ss_down(struct i2c_client *client, int chn); +static int ss_pre_freq_change(struct i2c_client *client, int chn); +static int ss_post_freq_change(struct i2c_client *client, int chn); + +static int calc_ss_down_to_regs(struct i2c_client *client, u32 * up_regs, u32 * down_regs, u32 * updown_reg, int chn); +static int get_ss_regs(struct i2c_client *client, u32 * up_regs, u32 * down_regs, u32 * updown_reg, int chn); +static int set_ss_regs(struct i2c_client *client, u32 * up_regs, u32 * down_regs, u32 * updown_reg, int chn); + static int disable_spread_spectrum(struct i2c_client *client,int chn); +static int enable_spread_spectrum(struct i2c_client *client,int chn); static int get_drv_powerdown(struct i2c_client *client, int chn); static int set_drv_powerdown(struct i2c_client *client, int typ, int chn); static int get_drv_disable(struct i2c_client *client, int chn); @@ -538,6 +650,7 @@ static s64 get_output_src_frequency(struct i2c_client *client, u64 *out_freq, in static int pre_init(struct i2c_client *client, int clear_all); static int post_init(struct i2c_client *client, int timeout); /*1 in timeout ~ 0.1ms - i2c read register */ +static int reset_ms(struct i2c_client *client, int wait_cycles); static int get_status(struct i2c_client *client); static int power_up_down_needed_ms(struct i2c_client *client); static int disable_output(struct i2c_client *client, int chn); @@ -680,6 +793,7 @@ static DEVICE_ATTR(ms0_powerdown, SYSFS_PERMISSIONS, ms_powerdown_show, ms_po static DEVICE_ATTR(ms1_powerdown, SYSFS_PERMISSIONS, ms_powerdown_show, ms_powerdown_store); static DEVICE_ATTR(ms2_powerdown, SYSFS_PERMISSIONS, ms_powerdown_show, ms_powerdown_store); static DEVICE_ATTR(ms3_powerdown, SYSFS_PERMISSIONS, ms_powerdown_show, ms_powerdown_store); +static DEVICE_ATTR(ms_reset, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, ms_reset_store); static struct attribute *multisynth_attrs[] = { &dev_attr_ms0_p123.attr, @@ -696,6 +810,7 @@ static struct attribute *multisynth_attrs[] = { &dev_attr_ms1_powerdown.attr, &dev_attr_ms2_powerdown.attr, &dev_attr_ms3_powerdown.attr, + &dev_attr_ms_reset.attr, NULL }; static const struct attribute_group dev_attr_multisynth_group = { @@ -703,6 +818,37 @@ static const struct attribute_group dev_attr_multisynth_group = { .name = "multiSynth", }; +/* Spread spectrum group */ +static DEVICE_ATTR(ss_change_freq_mode, SYSFS_PERMISSIONS, ss_change_freq_mode_show, ss_change_freq_mode_store); +static DEVICE_ATTR(ss0_values, SYSFS_PERMISSIONS, ss_vals_show, ss_vals_store); +static DEVICE_ATTR(ss1_values, SYSFS_PERMISSIONS, ss_vals_show, ss_vals_store); +static DEVICE_ATTR(ss2_values, SYSFS_PERMISSIONS, ss_vals_show, ss_vals_store); +static DEVICE_ATTR(ss3_values, SYSFS_PERMISSIONS, ss_vals_show, ss_vals_store); +static DEVICE_ATTR(ss0_regs_hex, SYSFS_PERMISSIONS, ss_regs_hex_show, ss_regs_hex_store); +static DEVICE_ATTR(ss1_regs_hex, SYSFS_PERMISSIONS, ss_regs_hex_show, ss_regs_hex_store); +static DEVICE_ATTR(ss2_regs_hex, SYSFS_PERMISSIONS, ss_regs_hex_show, ss_regs_hex_store); +static DEVICE_ATTR(ss3_regs_hex, SYSFS_PERMISSIONS, ss_regs_hex_show, ss_regs_hex_store); +static struct attribute *spread_spectrum_attrs[] = { + &dev_attr_ss_change_freq_mode.attr, + &dev_attr_ss0_values.attr, + &dev_attr_ss1_values.attr, + &dev_attr_ss2_values.attr, + &dev_attr_ss3_values.attr, + &dev_attr_ss0_regs_hex.attr, + &dev_attr_ss1_regs_hex.attr, + &dev_attr_ss2_regs_hex.attr, + &dev_attr_ss3_regs_hex.attr, + &dev_attr_ms_reset.attr, + NULL +}; +static const struct attribute_group dev_attr_spread_spectrum_group = { + .attrs = spread_spectrum_attrs, + .name = "spread_spectrum", +}; + + + + static DEVICE_ATTR(pre_init, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, pre_init_store); static DEVICE_ATTR(pre_init_clear, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, pre_init_store); static DEVICE_ATTR(post_init, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, post_init_store); @@ -1125,6 +1271,14 @@ static ssize_t output_description_show (struct device *dev, struct device_attrib buf+=rc; len+=rc; +//show spread spectum settings + rc=sprintf(buf,", "); + buf+=rc; + len+=rc; + if (((rc=get_ss_vals(dev, buf, i)))<0) return rc; + buf+=rc; + len+=rc; + rc=sprintf(buf,"\n"); buf+=rc; len+=rc; @@ -1228,6 +1382,7 @@ static int si5338_sysfs_register(struct device *dev) if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_multisynth_group)))<0) return retval; if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_pll_group)))<0) return retval; if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_output_group)))<0) return retval; + if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_spread_spectrum_group)))<0) return retval; #ifdef GENERATE_EXTRA if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_output_extra_group)))<0) return retval; #endif @@ -1676,6 +1831,124 @@ static ssize_t ms_powerdown_store(struct device *dev, struct device_attribute *a return count; } +static ssize_t ms_reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + reset_ms(client, 10); + return count; +} + +static ssize_t ss_change_freq_mode_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int mode; + struct i2c_client *client = to_i2c_client(dev); + struct si5338_data_t *clientdata = i2c_get_clientdata(client); + mode=clientdata->ss_on_freq_change; + switch (mode) { + case 0: return sprintf(buf, "%d - turn spread spectrum off on frequency change\n",mode); + case 1: return sprintf(buf, "%d - recalculate spread spectrum on frequency change, do not reset MS\n",mode); + case 2: return sprintf(buf, "%d - turn spread spectrum off on frequency change, reset MS when SS is turned on\n",mode); + case 3: return sprintf(buf, "%d - recalculate spread spectrum on frequency change, do not reset MS\n",mode); + default: return sprintf(buf, "%d - invalid mode\n",mode); + } +} + +static ssize_t ss_change_freq_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int num_items, mode; + struct i2c_client *client = to_i2c_client(dev); + struct si5338_data_t *clientdata = i2c_get_clientdata(client); + num_items=sscanf(buf, "%d", &mode); + if (num_items && (mode>=0) && (mode<=3)){ + clientdata->ss_on_freq_change=mode; + return count; + } + return -EINVAL; +} + +static ssize_t ss_vals_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int chn,len; + if (((chn=get_chn_from_name(attr->attr.name)))<0) return chn; + if (((len= get_ss_vals(dev, buf, chn)))<0) return len; + sprintf (buf+len,"\n"); + return len+1; +} + +static ssize_t ss_vals_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int chn, rc, state, num_items; + u32 rate,amp; + struct i2c_client *client = to_i2c_client(dev); + if (((chn=get_chn_from_name(attr->attr.name)))<0) return chn; + /* get current values */ + if (((state= get_ss_state(client, chn)))<0) return state; + if (((rate= get_ss_down_rate(client, chn)))<0) return rate; + if (((amp= get_ss_down_amplitude(client, chn)))<0) return amp; + num_items=sscanf(buf, "%d %d %d", &state, &rate, &); + if (num_items>1){ + if (((rc= store_ss_down_parameters(client, rate, amp, chn)))<0) return rc; + } + if (num_items>0){ + if (state) { + /* calculate and set SS registers */ + if (((rc=set_ss_down(client, chn)))<0) return rc; + /* enable SS, optionally reset MS */ + if (((rc=enable_spread_spectrum(client, chn)))<0) return rc; + } else { + if (((rc=disable_spread_spectrum(client, chn)))<0) return rc; + } + if (((rc=set_ss_state(client, 1, chn)))<0) return rc; +//static int enable_spread_spectrum(struct i2c_client *client,int chn) + + } + return count; +} + +static ssize_t ss_regs_hex_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int chn, rc; + u32 regs[7]; + u32 *updown_reg, *up_regs, *down_regs; + struct i2c_client *client = to_i2c_client(dev); + updown_reg=®s[0]; + down_regs=®s[1]; + up_regs=®s[4]; + + if (((chn=get_chn_from_name(attr->attr.name)))<0) return chn; + if (((rc= get_ss_regs(client, up_regs, down_regs, updown_reg, chn)))<0) return rc; + return sprintf(buf, "updown_par=0x%x down_pars=0x%x 0x%x 0x%x up_pars= 0x%x 0x%x 0x%x\n", + regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6]); +} + +static ssize_t ss_regs_hex_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int chn, rc, num_items; + u32 regs[7]; + u32 *updown_reg, *up_regs, *down_regs; + struct i2c_client *client = to_i2c_client(dev); + updown_reg=®s[0]; + down_regs=®s[1]; + up_regs=®s[4]; + + if (((chn=get_chn_from_name(attr->attr.name)))<0) return chn; + if (((rc= get_ss_regs(client, up_regs, down_regs, updown_reg, chn)))<0) return rc; + + num_items=sscanf(buf, "%x %x %x %x %x %x %x", ®s[0], ®s[1], ®s[2], ®s[3], ®s[4], ®s[5], ®s[6]); + if (num_items>0){ + if (num_items<5) up_regs=NULL; + if (num_items<2) down_regs=NULL; + if (((rc= set_ss_regs(client, up_regs, down_regs, updown_reg, chn)))<0) return rc; + } + return count; +} + + + + + + + static ssize_t pre_init_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int rc,clear_all; @@ -2311,13 +2584,218 @@ static int _verify_output_channel(struct i2c_client *client,int chn) return 0; } +static int get_ss_vals(struct device *dev, char * buf, int chn) +{ + int state; + u32 rate,amp; + struct i2c_client *client = to_i2c_client(dev); + if (((state= get_ss_state(client, chn)))<0) return state; + if (((amp= get_ss_down_amplitude(client, chn)))<0) return amp; + if (((rate= get_ss_down_rate(client, chn)))<0) return rate; + return sprintf(buf, "Spread spectrum is %s, down amplitude= %d ( *0.01%%), spread rate= %d Hz", state?"ON":"OFF", amp, rate); +} + +static int get_ss_state(struct i2c_client *client, int chn) +{ + int rc; + if (((rc=_verify_output_channel(client,chn)))<0) return rc; + return read_field(client, awe_ms_ssmode[chn]); +} + +static int set_ss_state(struct i2c_client *client, int state, int chn) +{ + int rc; + if (((rc=_verify_output_channel(client,chn)))<0) return rc; + return write_field(client, state , awe_ms_ssmode[chn]); +} + +static int get_ss_down_rate(struct i2c_client *client, int chn) +{ + int rc; + struct si5338_data_t *clientdata = i2c_get_clientdata(client); + if (((rc=_verify_output_channel(client,chn)))<0) return rc; + return clientdata->spread_spectrum_rate[chn]; /* in Hz */ +} + +static int get_ss_down_amplitude(struct i2c_client *client, int chn) +{ + int rc; + struct si5338_data_t *clientdata = i2c_get_clientdata(client); + if (((rc=_verify_output_channel(client,chn)))<0) return rc; + return clientdata->spread_spectrum_amp[chn]; +} + + +/* store required parameters - they will be needed to recalculate SS registers after MS frequency change */ +static int store_ss_down_parameters(struct i2c_client *client, u32 rate, u32 amp, int chn) /* chn 0,1,2,3 */ +{ + int rc; + struct si5338_data_t *clientdata = i2c_get_clientdata(client); + if (((rc=_verify_output_channel(client,chn)))<0) return rc; + if ((rate < SPREAD_RATE_MIN) || (rate > SPREAD_RATE_MAX)){ + dev_err(&client->dev, "Invalid spread spectrum rate %u - should be in [%u,%u]Hz\n",rate,SPREAD_RATE_MIN,SPREAD_AMP_MAX); + return - EINVAL; + } + if ((amp < SPREAD_AMP_MIN) || (amp > SPREAD_AMP_MAX)){ + dev_err(&client->dev, "Invalid spread spectrum amplitude %u - should be in [%u,%u] (*0.01%%)\n",amp,SPREAD_AMP_MIN,SPREAD_AMP_MAX); + return - EINVAL; + } + clientdata->spread_spectrum_rate[chn]=rate; /* in Hz */ + clientdata->spread_spectrum_amp[chn]=amp; /* in 0.01% */ + return 0; +} + +/* recalculate and set SS registers, disable SS if invalid */ +static int set_ss_down(struct i2c_client *client, int chn) /* chn 0,1,2,3 */ +{ + int rc; + u32 ssud,ssup[3],ssdn[3]; + if (((rc=_verify_output_channel(client,chn)))<0) return rc; + /* calculate spread spectrum registers */ + if (((rc=calc_ss_down_to_regs(client, ssup, ssdn, &ssud, chn)))<0) return rc; + if (rc!=0){ + return disable_spread_spectrum(client,chn); /* SS parameters were never set*/ + } + /* set spread spectrum registers */ + if (((rc=set_ss_regs(client, ssup, ssdn, &ssud, chn)))<0) return rc; +#if 0 + /* enable spread spectrum mode */ + if (((rc=enable_spread_spectrum(client, chn)))<0) return rc; +#endif + return 0; +} + +static int ss_pre_freq_change(struct i2c_client *client, int chn) /* chn 0,1,2,3 */ +{ + int rc; + struct si5338_data_t *clientdata = i2c_get_clientdata(client); + if (((rc=_verify_output_channel(client,chn)))<0) return rc; + if ((clientdata->ss_on_freq_change & 1)==0) { + dev_dbg(&client->dev, "Disabling spread spectrum before changing MS%d divider",chn); + if (((rc=disable_spread_spectrum(client, chn)))<0) return rc; + } + return 0; +} + +static int ss_post_freq_change(struct i2c_client *client, int chn) /* chn 0,1,2,3 */ +{ + int rc, ss_state; + struct si5338_data_t *clientdata = i2c_get_clientdata(client); + if (((rc=_verify_output_channel(client,chn)))<0) return rc; + if (((ss_state=get_ss_state(client,chn)))<0) return ss_state; + if (ss_state){ + /* recalculate and set SS registers */ + dev_dbg(&client->dev, "Recalculating spread spectrum after changing MS%d divider",chn); + if (((rc=set_ss_down(client,chn)))<0) return rc; + if (clientdata->ss_on_freq_change & 2) { + reset_ms(client, 10); + } + } + return 0; +} + + +static int calc_ss_down_to_regs(struct i2c_client *client, u32 * up_regs, u32 * down_regs, u32 * updown_reg, int chn) /* chn 0,1,2,3 */ +{ + int rc; + u32 ssud; + u64 ms_freq, xy[2]; + u32 p123[3]; + u64 ms[3]; + u32 rate, amp; + struct si5338_data_t *clientdata = i2c_get_clientdata(client); + if (((rc=_verify_output_channel(client,chn)))<0) return rc; + rate=clientdata->spread_spectrum_rate[chn]; /* in Hz */ + amp=clientdata->spread_spectrum_amp[chn]; /* in 0.01% */ + + if ((rate==0) || (amp==0)) return 1; /* Should disable SS */ + + if ((rate < SPREAD_RATE_MIN) || (rate > SPREAD_RATE_MAX)){ + dev_err(&client->dev, "*Invalid spread spectrum rate %u - should be in [%u,%u]Hz\n",rate,SPREAD_RATE_MIN,SPREAD_AMP_MAX); + return - EINVAL; + } + if ((amp < SPREAD_AMP_MIN) || (amp > SPREAD_AMP_MAX)){ + dev_err(&client->dev, "*Invalid spread spectrum amplitude %u - should be in [%u,%u] (*0.01%%)\n",amp,SPREAD_AMP_MIN,SPREAD_AMP_MAX); + return - EINVAL; + } + if (((rc=get_pll_ms_freq(client, &ms_freq, chn)))<0) return rc; + if (ms_freq==0){ + dev_err(&client->dev, "MS%d frequency is not set, can not apply spread spectrum\n",chn); + return - EINVAL; + } + if (((rc=get_ms_p123(client,p123, chn)))<0) return rc; + p123_to_ms(ms,p123); + ssud=(u32) div64_u64(ms_freq,rate << 2); + if (updown_reg) updown_reg[0]= ssud; + if (down_regs){ + xy[0]=6400000000LL*(ms[0]*ms[2]+ms[1]); + xy[0]=div64_u64(xy[0],ms[2]); + xy[1]= 100000000LL*(10000-amp)*ssud; + remove_common_factor(xy); + down_regs[0]= (u32) div64_u64(xy[0],xy[1]); + down_regs[2]= (u32)xy[1]; + down_regs[1]= (u32)xy[0] - down_regs[2]*down_regs[0]; + } + if (up_regs){ + up_regs[0]=0; + up_regs[1]=1; + up_regs[2]=0; + } + return 0; +} + +static int get_ss_regs(struct i2c_client *client, u32 * up_regs, u32 * down_regs, u32 * updown_reg, int chn) /* chn 0,1,2,3 */ +{ + int i; + s64 rc; + if (((rc=_verify_output_channel(client,chn)))<0) return (int) rc; + if (up_regs) for (i=0;i<3;i++){ + if (((rc=read_multireg64 (client, awe_msx_ssup[chn][i])))<0) return (int) rc; + up_regs[i]= (u32) rc; + } + if (down_regs) for (i=0;i<3;i++){ + if (((rc=read_multireg64 (client, awe_msx_ssdn[chn][i])))<0) return (int) rc; + down_regs[i]= (u32) rc; + } + if (updown_reg) { + if (((updown_reg[0]=read_multireg64 (client, awe_msx_ssud[chn])))<0) return (int) updown_reg[0]; + } + return 0; +} +static int set_ss_regs(struct i2c_client *client, u32 * up_regs, u32 * down_regs, u32 * updown_reg, int chn) /* chn 0,1,2,3, */ +{ + int i,rc; + if (((rc=_verify_output_channel(client,chn)))<0) return rc; + if (up_regs) for (i=0;i<3;i++){ + if (((rc=write_multireg64 (client, (u64) up_regs[i], awe_msx_ssup[chn][i])))<0) return rc; + } + if (down_regs) for (i=0;i<3;i++){ + if (((rc=write_multireg64 (client, (u64) down_regs[i], awe_msx_ssdn[chn][i])))<0) return rc; + } + if (updown_reg) { + if (((rc=write_multireg64 (client, (u64) updown_reg[0], awe_msx_ssud[chn])))<0) return rc; + } + return 0; +} + static int disable_spread_spectrum(struct i2c_client *client,int chn) { int rc; if (((rc=_verify_output_channel(client,chn)))<0) return rc; /* disable spread spectrum - only this register was changed to 0 from default 1 */ - if (((rc=write_multireg64(client, 0 , awe_msx_ssup3[chn])))<0) return rc; - if (((rc=write_field(client, 0 , awe_ms_ssmode[chn])))<0) return rc; + if (((rc=write_multireg64(client, 0 , awe_msx_ssup[chn][2])))<0) return rc; + if (((rc=set_ss_state(client, 0, chn)))<0) return rc; + return 0; +} +static int enable_spread_spectrum(struct i2c_client *client,int chn) +{ + int rc; + struct si5338_data_t *clientdata = i2c_get_clientdata(client); + if (((rc=_verify_output_channel(client,chn)))<0) return rc; + if (((rc=set_ss_state(client, 1, chn)))<0) return rc; + if (clientdata->ss_on_freq_change & 2) { + if (((rc=reset_ms(client, 10)))<0) return rc; /* 10 - wait cycles */ + } return 0; } @@ -2855,15 +3333,23 @@ static int post_init(struct i2c_client *client, int timeout) /*1 in timeout ~ 0. if (((rc=write_field(client, 5, AWE_REG47_72 )))<0) return rc; /* set 47[7:2] to 000101b */ if (((rc=write_field(client, 1, AWE_FCAL_OVRD_EN )))<0) return rc; /* SET PLL to use FCAL values, set FCAL_OVRD_EN=1 */ /* only needed if using down-spread. Won't hurt to do anyway */ - if (((rc=write_field(client, 1, AWE_MS_RESET )))<0) return rc; /* SET MS RESET=1 */ - for (i=0;i<10;i++) get_status(client); /* wait 1 ms */ - if (((rc=write_field(client, 0, AWE_MS_RESET )))<0) return rc; /* SET MS RESET=0 */ + if (((rc=reset_ms(client, 10)))<0) return rc; if (((rc=write_field(client, 0, AWE_OUT_ALL_DIS )))<0) return rc; /* enable all (enabled individually) outputs */ write_field(client, 0, AWE_SOFT_RESET ); /* Not documented - what to do with the soft reset bit - clearing */ if (((rc=power_up_down_needed_ms(client)))<0) return rc; return 0; } +static int reset_ms(struct i2c_client *client, int wait_cycles) +{ + int i,rc; + dev_dbg(&client->dev, "Resetting MS dividers"); + if (((rc=write_field(client, 1, AWE_MS_RESET )))<0) return rc; /* SET MS RESET=1 */ + for (i=0;idev, "Using high speed divisor option on channel",chn); + dev_info(&client->dev, "Using high speed divisor option on ms%d",chn); } else hs=0; if (((rc=write_field(client, hs, awe_ms_hs[chn])))<0) return rc; - + /* optionally disable spread spectrum before changing frequency */ + if (chn<4){ + if (((rc=ss_pre_freq_change(client, chn)))<0) return rc; + } for (i=0;i<3;i++){ if (((rc=write_multireg64(client, (u64) p123[i], awe_msx[chn][i])))<0) return rc; } + /* optionally enable spread spectrum after changing frequency, reset MS */ + if (chn<4){ + if (((rc=ss_post_freq_change(client, chn)))<0) return rc; + } return 0; } @@ -3852,6 +4345,7 @@ static void si5338_init_of(struct i2c_client *client) u16 page_reg; char buf[40]; u64 freq[3]; + u32 rate,amp; struct si5338_setup_data { u8 page; u8 reg; @@ -3998,6 +4492,34 @@ static void si5338_init_of(struct i2c_client *client) } } } + /* setting spread spectrum parameters */ + for (n=0;n<4;n++){ + sprintf(buf,"si5338,spread_spectrum_%d",n); + config_data = of_get_property(client->dev.of_node, buf, &len); + if (config_data && (len>0)){ + len /= sizeof(*config_data); + rate=get_ss_down_rate(client, n); + amp= get_ss_down_amplitude(client, n); + if (len>1) amp = be32_to_cpup(&config_data[1]); + if (len>2) rate = be32_to_cpup(&config_data[2]); + if (store_ss_down_parameters(client, rate, amp, n)==0){ + dev_info(&client->dev,"Set spread spectrum parameters for MS%d, amplitude=%d (*0.01%%), rate=%d Hz, %s\n", + n,amp,rate,config_data[0]?"ON":"OFF"); + } else { + dev_err(&client->dev,"Failed to set spread spectrum parameters for MS%d, amplitude=%d (*0.01%%), rate=%d Hz, %s\n", + n,amp,rate,config_data[0]?"ON":"OFF"); + continue; + } + if (config_data[0]){ /* enable SS */ + if ((set_ss_down(client, n)==0) && /* calculate and set SS registers */ + (set_ss_state(client, 1, n)==0)){ // enable SS. Not using enable_spread_spectrum() as we'll reset MS later anyway + dev_info(&client->dev,"Spread spectrum enabled for MS%d\n",n); + } else { + dev_info(&client->dev,"Fail to enable spread spectrum for MS%d\n",n); + } + } + } + } } else { dev_info(&client->dev,"Device tree data not found for %s\n",client->name); } @@ -4056,11 +4578,16 @@ static int si5338_i2c_probe(struct i2c_client *client, clientdata->input_frequency3=0; clientdata->input_frequency4=0; clientdata->input_frequency56=0; + clientdata->ss_on_freq_change=0; /* 0 - disable SS when frequency is changed, 1 - update SS. +2 reset MS after starting SS*/ + + for (i=0;i<4;i++){ + clientdata->spread_spectrum_rate[i]=SPREAD_RATE_DFLT; /* 31.5 KHz */ + clientdata->spread_spectrum_amp[i]=SPREAD_AMP_DFLT; /* 0.5% */ + } si5338_init_of(client); return 0; } - static int si5338_i2c_remove(struct i2c_client *client) { return 0; -- 2.18.1