Initial Check in to manually control Frequency ( Auto Voltage ) from User Space
Change-Id: I0e8b80d1ce661fe48ebf977bf55f6e9e0b2f4ef3
Signed-off-by: wthomas <wthomas@codeaurora.org>
Reviewed-by: Bob Amstadt <ramstadt@codeaurora.org>
diff --git a/nss_api_if.h b/nss_api_if.h
index 9f2070e..78b5633 100755
--- a/nss_api_if.h
+++ b/nss_api_if.h
@@ -1029,5 +1029,15 @@
*/
extern void nss_unregister_tun6rd_if(uint32_t if_num);
+/*
+ * @brief NSS Frequency Change
+ * @ param ctx NSS context
+ * @ param eng Frequency Value in Hz
+ * @ param start_or_end Start or End of Freq Change
+ *
+ * @return nss_tx_status_t Tx Status
+ */
+nss_tx_status_t nss_freq_change(void *ctx, uint32_t eng, uint32_t start_or_end);
+
/**@}*/
#endif /** __NSS_API_IF_H */
diff --git a/nss_core.h b/nss_core.h
index 254bcac..d0f5b92 100755
--- a/nss_core.h
+++ b/nss_core.h
@@ -553,6 +553,17 @@
#endif
/*
+ * NSS Statistics and Data for User Space
+ */
+struct nss_cmd_buffer {
+ int32_t current_freq;
+ int32_t auto_scale;
+ int32_t max_freq;
+ uint32_t register_addr;
+ uint32_t register_data;
+};
+
+/*
* APIs provided by nss_core.c
*/
extern int nss_core_handle_napi(struct napi_struct *napi, int budget);
diff --git a/nss_hal/ipq806x/nss_hal_pvt.c b/nss_hal/ipq806x/nss_hal_pvt.c
index d40bb6a..2f58ed0 100755
--- a/nss_hal/ipq806x/nss_hal_pvt.c
+++ b/nss_hal/ipq806x/nss_hal_pvt.c
@@ -6,6 +6,7 @@
*/
#include <linux/delay.h>
+#include <linux/err.h>
#include "nss_hal_pvt.h"
#include "nss_clocks.h"
@@ -27,17 +28,205 @@
return readl(addr);
}
-#if defined(NSS_ENABLE_CLK)
+/*
+ * nss_hal_pvt_pll_change
+ * Change the Pll between 11(400mhz) or 18(1066 or 1466)
+ */
+void nss_hal_pvt_pll_change(uint32_t pll)
+{
+ uint32_t ns_reg0;
+ uint32_t ns_reg1;
+
+ uint32_t pll11_mask = 0x3;
+ uint32_t pll18_mask = 0x1;
+
+ uint32_t pll_cl_mask = 0x7;
+
+
+ printk("Picking PLL%d\n", pll);
+
+ if (pll == 11) {
+ ns_reg0 = clk_reg_read_32(UBI32_COREn_CLK_SRC0_NS(0));
+ ns_reg1 = clk_reg_read_32(UBI32_COREn_CLK_SRC1_NS(1));
+
+ ns_reg0 &= ~pll_cl_mask;
+ ns_reg1 &= ~pll_cl_mask;
+
+ ns_reg0 |= pll11_mask;
+ ns_reg1 |= pll11_mask;
+
+ clk_reg_write_32(UBI32_COREn_CLK_SRC0_NS(0), ns_reg0);
+ clk_reg_write_32(UBI32_COREn_CLK_SRC0_NS(1), ns_reg1);
+
+
+ } else if (pll == 18) {
+ ns_reg0 = clk_reg_read_32(UBI32_COREn_CLK_SRC0_NS(0));
+ ns_reg1 = clk_reg_read_32(UBI32_COREn_CLK_SRC1_NS(1));
+
+ ns_reg0 &= ~pll_cl_mask;
+ ns_reg1 &= ~pll_cl_mask;
+
+ ns_reg0 |= pll18_mask;
+ ns_reg1 |= pll18_mask;
+
+ clk_reg_write_32(UBI32_COREn_CLK_SRC0_NS(0), ns_reg0);
+ clk_reg_write_32(UBI32_COREn_CLK_SRC0_NS(1), ns_reg1);
+
+ }
+
+ return;
+}
+
+/*
+ * nss_hal_pvt_divide_pll
+ * Divide PLL by int val
+ */
+uint32_t nss_hal_pvt_divide_pll(uint32_t core_id, uint32_t pll, uint32_t divider)
+{
+ uint32_t ns_mask = 0x00ff01ff;
+ uint32_t ns_mask_1 = 0x00ff0001;
+ uint32_t ns_mask_2 = 0x00fe0141;
+ uint32_t ns_mask_5 = 0x00fb0141;
+ uint32_t ns_reg0;
+ uint32_t ns_reg1;
+
+ uint32_t md_mask = 0x00ff00ff;
+ uint32_t md_mask_2 = 0x000100fd;
+ uint32_t md_mask_5 = 0x000100fa;
+ uint32_t md_reg0;
+ uint32_t md_reg1;
+
+#if (NSS_DEBUG_LEVEL > 0)
+ printk("NSSFB0_CLK_SRC_CTL : %x\n", clk_reg_read_32(NSSFB0_CLK_SRC_CTL));
+ printk("NSSFB1_CLK_SRC_CTL : %x\n", clk_reg_read_32(NSSFB1_CLK_SRC_CTL));
+ printk("NSSFB0_CLK_SRC0_NS : %x\n", clk_reg_read_32(NSSFB0_CLK_SRC0_NS));
+ printk("NSSFB0_CLK_SRC1_NS : %x\n", clk_reg_read_32(NSSFB0_CLK_SRC1_NS));
+ printk("NSSFB1_CLK_SRC0_NS : %x\n", clk_reg_read_32(NSSFB1_CLK_SRC0_NS));
+ printk("NSSFB1_CLK_SRC1_NS : %x\n", clk_reg_read_32(NSSFB1_CLK_SRC1_NS));
+ printk("PLL_ENA_NSS : %x\n", clk_reg_read_32(PLL_ENA_NSS));
+ printk("\n");
+ printk("PLL18_L_VAL : %x\n", clk_reg_read_32(PLL18_L_VAL));
+ printk("PLL18_M_VAL : %x\n", clk_reg_read_32(PLL18_M_VAL));
+ printk("PLL18_N_VAL : %x\n", clk_reg_read_32(PLL18_N_VAL));
+ printk("PLL18_CONFIG : %x\n", clk_reg_read_32(PLL18_CONFIG));
+ printk("PLL18_TEST_CTL: %x\n", clk_reg_read_32(PLL18_TEST_CTL));
+ printk("\n");
+ printk("UBI32_COREn_CLK_SRC0_CTL Core 0: %x\n", clk_reg_read_32(UBI32_COREn_CLK_SRC_CTL(0)));
+ printk("UBI32_COREn_CLK_SRC0_CTL Core 1: %x\n", clk_reg_read_32(UBI32_COREn_CLK_SRC_CTL(1)));
+ printk("UBI32_COREn_CLK_SRC0_NS Core 0: %x\n", clk_reg_read_32(UBI32_COREn_CLK_SRC0_NS(0)));
+ printk("UBI32_COREn_CLK_SRC0_NS Core 1: %x\n", clk_reg_read_32(UBI32_COREn_CLK_SRC0_NS(1)));
+ printk("UBI32_COREn_CLK_SRC0_MD Core 0: %x\n", clk_reg_read_32(UBI32_COREn_CLK_SRC0_MD(0)));
+ printk("UBI32_COREn_CLK_SRC0_MD Core 1: %x\n", clk_reg_read_32(UBI32_COREn_CLK_SRC0_MD(1)));
+ printk("\n\n\n");
+#endif
+
+ md_reg0 = clk_reg_read_32(UBI32_COREn_CLK_SRC0_MD(0));
+ md_reg1 = clk_reg_read_32(UBI32_COREn_CLK_SRC0_MD(1));
+ ns_reg0 = clk_reg_read_32(UBI32_COREn_CLK_SRC0_NS(0));
+ ns_reg1 = clk_reg_read_32(UBI32_COREn_CLK_SRC0_NS(1));
+
+ /*
+ * Bypass
+ */
+ if (divider == 1) {
+ printk("Bypass PLL Output\n");
+
+ /*
+ * Clear M and D ( Not2*D ) and Set Bits
+ */
+
+ md_reg0 &= ~md_mask;
+ md_reg1 &= ~md_mask;
+
+ /*
+ * PLL Source/ Pre Divide/ Counter Mode/ Counter Reset/ Counter Enable/ N Value
+ */
+
+ ns_reg0 &= ~ns_mask;
+ ns_reg1 &= ~ns_mask;
+
+ ns_reg0 |= ns_mask_1;
+ ns_reg1 |= ns_mask_1;
+ } else if (divider == 2) {
+
+ printk("Divide PLL Output by 2\n");
+
+ /*
+ * Clear M and D ( Not2*D ) and Set Bits
+ */
+
+ md_reg0 &= ~md_mask;
+ md_reg1 &= ~md_mask;
+
+ md_reg0 |= md_mask_2;
+ md_reg1 |= md_mask_2;
+
+ /*
+ * PLL Source/ Pre Divide/ Counter Mode/ Counter Reset/ Counter Enable/ N Value
+ */
+
+ ns_reg0 &= ~ns_mask;
+ ns_reg1 &= ~ns_mask;
+
+ ns_reg0 |= ns_mask_2;
+ ns_reg1 |= ns_mask_2;
+ } else if (divider == 5) {
+
+ printk("Divide PLL Output by 5\n");
+
+ /*
+ * Clear M and D ( Not2*D ) and Set Bits
+ */
+
+ md_reg0 &= ~md_mask;
+ md_reg1 &= ~md_mask;
+
+ md_reg0 |= md_mask_5;
+ md_reg1 |= md_mask_5;
+
+ /*
+ * PLL Source/ Pre Divide/ Counter Mode/ Counter Reset/ Counter Enable/ N Value
+ */
+
+ ns_reg0 &= ~ns_mask;
+ ns_reg1 &= ~ns_mask;
+
+ ns_reg0 |= ns_mask_5;
+ ns_reg1 |= ns_mask_5;
+ }
+
+ nss_hal_pvt_pll_change(pll);
+
+ clk_reg_write_32(UBI32_COREn_CLK_SRC0_MD(0), md_reg0);
+ clk_reg_write_32(UBI32_COREn_CLK_SRC0_MD(1), md_reg1);
+ clk_reg_write_32(UBI32_COREn_CLK_SRC0_NS(0), ns_reg0);
+ clk_reg_write_32(UBI32_COREn_CLK_SRC0_NS(1), ns_reg1);
+
+#if (NSS_DEBUG_LEVEL > 0)
+ printk("UBI32_COREn_CLK_SRC0_CTL Core 0: %x\n", clk_reg_read_32(UBI32_COREn_CLK_SRC_CTL(0)));
+ printk("UBI32_COREn_CLK_SRC0_CTL Core 1: %x\n", clk_reg_read_32(UBI32_COREn_CLK_SRC_CTL(1)));
+ printk("UBI32_COREn_CLK_SRC0_NS Core 0: %x\n", clk_reg_read_32(UBI32_COREn_CLK_SRC0_NS(0)));
+ printk("UBI32_COREn_CLK_SRC0_NS Core 1: %x\n", clk_reg_read_32(UBI32_COREn_CLK_SRC0_NS(1)));
+ printk("UBI32_COREn_CLK_SRC0_MD Core 0: %x\n", clk_reg_read_32(UBI32_COREn_CLK_SRC0_MD(0)));
+ printk("UBI32_COREn_CLK_SRC0_MD Core 1: %x\n", clk_reg_read_32(UBI32_COREn_CLK_SRC0_MD(1)));
+#endif
+
+ return 1;
+}
/*
* nss_hal_pvt_enable_pll18()
* Enable PLL18
*/
-static uint32_t nss_hal_pvt_enable_pll18(void)
+uint32_t nss_hal_pvt_enable_pll18(uint32_t speed)
{
uint32_t wait_cycles = 100;
+
+ /*
+ * Prevent Compiler from commenting out the loop.
+ */
volatile uint32_t value;
- volatile uint32_t mask = (1 << 18);
+ volatile uint32_t mask = (1 << 2);
/*
* Start with clean slate
@@ -45,18 +234,33 @@
clk_reg_write_32(PLL18_MODE, 0);
/*
- * Set L = 44, M = 0, N = 1
- * Effective VCO Frequency = 1100 MHz
+ * Effective VCO Frequency = 1100 MHz Post Divide 2
*/
- clk_reg_write_32(PLL18_L_VAL, 0x4000042C);
- clk_reg_write_32(PLL18_M_VAL, 0x0);
- clk_reg_write_32(PLL18_N_VAL, 0x1);
+ if (speed == 1100) {
+ clk_reg_write_32(PLL18_L_VAL, 0x4000042C);
+ clk_reg_write_32(PLL18_M_VAL, 0x0);
+ clk_reg_write_32(PLL18_N_VAL, 0x1);
- /*
- * PLL configuration (as provided by HW team)
- */
- clk_reg_write_32(PLL18_CONFIG, 0x01495625);
- clk_reg_write_32(PLL18_TEST_CTL, 0x00003080);
+ /*
+ * PLL configuration (as provided by HW team)
+ */
+ clk_reg_write_32(PLL18_CONFIG, 0x01495625);
+ clk_reg_write_32(PLL18_TEST_CTL, 0x00003080);
+ } else if (speed == 1466) {
+ /*
+ * Effective VCO Frequency = 1466 MHz Post Divide 2
+ */
+
+ clk_reg_write_32(PLL18_L_VAL, 0x4000043A);
+ clk_reg_write_32(PLL18_M_VAL, 0x10);
+ clk_reg_write_32(PLL18_N_VAL, 0x19);
+
+ /*
+ * PLL configuration (as provided by HW team)
+ */
+ clk_reg_write_32(PLL18_CONFIG, 0x014B5625);
+ clk_reg_write_32(PLL18_TEST_CTL, 0x00003080);
+ }
/*
* Enable PLL18 output (sequence provided by HW team)
@@ -82,7 +286,6 @@
return PLL_NOT_LOCKED;
}
-#endif
/*
* __nss_hal_common_reset
@@ -91,9 +294,10 @@
void __nss_hal_common_reset(uint32_t *clk_src)
{
uint32_t i;
-#if defined(NSS_ENABLE_CLK)
uint32_t pll18_status;
+#if defined(NSS_ENABLE_CLK)
+
/*
* NSS FPB CLOCK
*/
@@ -202,6 +406,18 @@
*/
*clk_src = NSS_REGS_CLK_SRC_DEFAULT;
#endif
+ pll18_status = nss_hal_pvt_enable_pll18(1100);
+
+ if (!pll18_status) {
+ /*
+ * Select alternate good source (Src1/pll0)
+ */
+ printk("Enable PLL18 Failed, Using Alternate");
+ *clk_src = NSS_REGS_CLK_SRC_ALTERNATE;
+ } else {
+ nss_hal_pvt_divide_pll(0, 18, 1);
+ }
+
/*
* Attach debug interface to TLMM
*/
diff --git a/nss_hal/ipq806x/nss_hal_pvt.h b/nss_hal/ipq806x/nss_hal_pvt.h
index 51c2905..c4a092b 100755
--- a/nss_hal/ipq806x/nss_hal_pvt.h
+++ b/nss_hal/ipq806x/nss_hal_pvt.h
@@ -58,5 +58,8 @@
extern void __nss_hal_core_reset(uint32_t core_id, uint32_t map, uint32_t addr, uint32_t clk_src);
extern void __nss_hal_common_reset(uint32_t *clk_src);
+extern uint32_t nss_hal_pvt_divide_pll(uint32_t core_id, uint32_t pll, uint32_t divider);
+extern void nss_hal_pvt_pll_change(uint32_t pll);
+extern uint32_t nss_hal_pvt_enable_pll18(uint32_t speed);
#endif /* __NSS_HAL_PVT_H */
diff --git a/nss_hlos_if.h b/nss_hlos_if.h
index e865043..db2aa51 100755
--- a/nss_hlos_if.h
+++ b/nss_hlos_if.h
@@ -257,6 +257,14 @@
};
/*
+ * The NSS freq start or stop strcture
+ */
+struct nss_freq_change {
+ uint32_t frequency;
+ uint32_t start_or_end;
+};
+
+/*
* Types of TX metadata.
*/
enum nss_tx_metadata_types {
@@ -280,8 +288,12 @@
NSS_TX_METADATA_TYPE_MSS_SET,
NSS_TX_METADATA_TYPE_C2C_TX_MAP,
NSS_TX_METADATA_TYPE_IPSEC_RULE,
+ NSS_TX_METADATA_TYPE_IPSEC_TX_RULE_DESTROY,
+ NSS_TX_METADATA_TYPE_IPSEC_RX_RULE_CREATE,
+ NSS_TX_METADATA_TYPE_IPSEC_RX_RULE_DESTROY,
NSS_TX_METADATA_TYPE_PROFILER_TX,
NSS_TX_METADATA_TYPE_GENERIC_IF_PARAMS,
+ NSS_TX_METADATA_TYPE_NSS_FREQ_CHANGE,
};
/*
@@ -310,9 +322,18 @@
struct nss_ipsec_rule ipsec_rule;
struct nss_profiler_tx profiler_tx;
struct nss_generic_if_params generic_if_params;
+ struct nss_freq_change freq_change;
} sub;
};
+/*
+ * The NSS freq ack structure
+ */
+struct nss_freq_ack {
+ uint32_t freq_current;
+ int32_t ack_status;
+};
+
struct nss_port_info {
uint8_t num_phys_ports;
};
@@ -764,6 +785,7 @@
NSS_RX_METADATA_TYPE_NSS_STATS_SYNC,
NSS_RX_METADATA_TYPE_PPPOE_STATS_SYNC,
NSS_RX_METADATA_TYPE_PROFILER_SYNC,
+ NSS_RX_METADATA_TYPE_FREQ_ACK,
};
/*
@@ -783,6 +805,7 @@
struct nss_nss_stats_sync nss_stats_sync;
struct nss_pppoe_exception_stats_sync pppoe_exception_stats_sync;
struct nss_profiler_sync profiler_sync;
+ struct nss_freq_ack freq_ack;
} sub;
};
diff --git a/nss_init.c b/nss_init.c
index a5161d8..46ad2b1 100755
--- a/nss_init.c
+++ b/nss_init.c
@@ -11,8 +11,13 @@
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/device.h>
#include <mach/msm_nss.h>
+#include <linux/sysctl.h>
+#include <linux/regulator/consumer.h>
+
/*
* Declare module parameters
*/
@@ -45,10 +50,23 @@
* Global declarations
*/
+
+/*
+ * Define for RPM Nominal and Turbo for NSS
+ */
+#define NSS_NOM_VCC 1050000
+#define NSS_TURB_VCC 1150000
+
+/*
+ * Global Handlers for the nss regulators
+ */
+struct regulator *nss0_vreg;
+
/*
* Top level nss context structure
*/
struct nss_top_instance nss_top_main;
+struct nss_cmd_buffer nss_cmd_buf;
/*
* File local/Static variables/functions
@@ -101,6 +119,40 @@
nss_ctx->nss_top = nss_top;
nss_ctx->id = nss_dev->id;
+ nss_info("%p: NSS_DEV_ID %s \n", nss_ctx, dev_name(&nss_dev->dev));
+
+ /*
+ * Both NSS cores controlled by same regulator, Hook only Once
+ */
+ if (!nss_dev->id) {
+ nss0_vreg = devm_regulator_get(&nss_dev->dev, "VDD_UBI0");
+
+ if (IS_ERR(nss0_vreg)) {
+
+ err = PTR_ERR(nss0_vreg);
+ nss_info("%p: Regulator %s get failed, err=%d\n", nss_ctx, dev_name(&nss_dev->dev), err);
+ return err;
+
+ } else {
+
+ nss_info("%p: Regulator %s get success\n", nss_ctx, dev_name(&nss_dev->dev));
+
+ err = regulator_enable(nss0_vreg);
+ if (err) {
+ nss_info("%p: Regulator %s enable voltage failed, err=%d\n", nss_ctx, dev_name(&nss_dev->dev), err);
+ return err;
+ }
+
+ err = regulator_set_voltage(nss0_vreg, NSS_NOM_VCC, NSS_NOM_VCC);
+ if (err) {
+ nss_info("%p: Regulator %s set voltage failed, err=%d\n", nss_ctx, dev_name(&nss_dev->dev), err);
+ return err;
+ }
+
+
+ }
+ }
+
/*
* Get virtual and physical memory addresses for nss logical/hardware address maps
*/
@@ -365,6 +417,176 @@
};
/*
+ * nss_current_freq_handler()
+ * Handle Userspace Frequency Change Requests
+ */
+static int nss_current_freq_handler (ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ void *ubicom_na_nss_context = NULL;
+ int ret;
+ int vret;
+
+ ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
+
+ if (write) {
+
+ ubicom_na_nss_context = nss_register_ipv4_mgr(NULL);
+
+ nss_info("Frequency Set to %d\n", nss_cmd_buf.current_freq);
+
+ /* If support NSS freq is in the table send the new frequency request to NSS */
+
+ if (nss_cmd_buf.current_freq == 110000000) {
+
+ nss_freq_change(ubicom_na_nss_context, 533000000, 0);
+ nss_hal_pvt_divide_pll(0, 11, 1);
+ nss_hal_pvt_enable_pll18(1100);
+ nss_freq_change(ubicom_na_nss_context, nss_cmd_buf.current_freq, 0);
+ nss_hal_pvt_divide_pll(0, 18, 5);
+
+ vret = regulator_set_voltage(nss0_vreg, NSS_NOM_VCC, NSS_NOM_VCC);
+ if (vret) {
+ nss_info("Regulator set voltage failed, err=%d\n", vret);
+ return ret;
+ }
+
+ } else if (nss_cmd_buf.current_freq == 225000000) {
+
+ nss_freq_change(ubicom_na_nss_context, 533000000, 0);
+ nss_hal_pvt_divide_pll(0, 11, 1);
+ nss_hal_pvt_enable_pll18(1100);
+ nss_freq_change(ubicom_na_nss_context, nss_cmd_buf.current_freq, 0);
+ nss_hal_pvt_divide_pll(0, 18, 2);
+
+ vret = regulator_set_voltage(nss0_vreg, NSS_NOM_VCC, NSS_NOM_VCC);
+ if (vret) {
+ nss_info("Regulator set voltage failed, err=%d\n", vret);
+ return ret;
+ }
+
+ } else if (nss_cmd_buf.current_freq == 550000000) {
+
+ nss_freq_change(ubicom_na_nss_context, 533000000, 0);
+ nss_hal_pvt_divide_pll(0, 11, 1);
+ nss_hal_pvt_enable_pll18(1100);
+ nss_freq_change(ubicom_na_nss_context, nss_cmd_buf.current_freq, 0);
+ nss_hal_pvt_divide_pll(0, 18, 1);
+
+ vret = regulator_set_voltage(nss0_vreg, NSS_NOM_VCC, NSS_NOM_VCC);
+ if (vret) {
+ nss_info("Regulator set voltage failed, err=%d\n", vret);
+ return ret;
+ }
+
+ } else if (nss_cmd_buf.current_freq == 733000000) {
+ vret = regulator_set_voltage(nss0_vreg, NSS_TURB_VCC, NSS_TURB_VCC);
+ if (vret) {
+ nss_info("Regulator set voltage failed, err=%d\n", vret);
+ return ret;
+ }
+
+ nss_freq_change(ubicom_na_nss_context, 533000000, 0);
+ nss_hal_pvt_divide_pll(0, 11, 1);
+ nss_hal_pvt_enable_pll18(1466);
+ nss_freq_change(ubicom_na_nss_context, nss_cmd_buf.current_freq, 0);
+ nss_hal_pvt_divide_pll(0, 18, 1);
+
+ } else {
+ nss_info("Frequency not found. Please check Frequency Table\n");
+ }
+ }
+ return ret;
+}
+
+/*
+ * nss_auto_scale_handler()
+ * Enables or Disable Auto Scaling
+ */
+static int nss_auto_scale_handler (ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ int ret;
+
+ ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
+
+ printk("Not Supported\n");
+
+ return ret;
+}
+
+/*
+ * nss_get_freq_table_handler()
+ * Display Support Freq and Ex how to Change.
+ */
+static int nss_get_freq_table_handler (ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ int ret;
+
+ ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
+
+ printk("Frequency Supported - 110Mhz 225Mhz 550Mhz 733Mhz \n");
+ printk("Ex. To Change Frequency - echo 110000000 > current_freq \n");
+
+ return ret;
+}
+
+/*
+ * sysctl-tuning infrastructure.
+ */
+static ctl_table nss_freq_table[] = {
+ {
+ .procname = "current_freq",
+ .data = &nss_cmd_buf.current_freq,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &nss_current_freq_handler,
+ },
+ {
+ .procname = "freq_table",
+ .data = &nss_cmd_buf.max_freq,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &nss_get_freq_table_handler,
+ },
+ {
+ .procname = "auto_scale",
+ .data = &nss_cmd_buf.auto_scale,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &nss_auto_scale_handler,
+ },
+ { }
+};
+
+static ctl_table nss_clock_dir[] = {
+ {
+ .procname = "clock",
+ .mode = 0555,
+ .child = nss_freq_table,
+ },
+ { }
+};
+
+static ctl_table nss_root_dir[] = {
+ {
+ .procname = "nss",
+ .mode = 0555,
+ .child = nss_clock_dir,
+ },
+ { }
+};
+
+static ctl_table nss_root[] = {
+ {
+ .procname = "dev",
+ .mode = 0555,
+ .child = nss_root_dir,
+ },
+ { }
+};
+
+static struct ctl_table_header *nss_dev_header;
+
+/*
* nss_init()
* Registers nss driver
*/
@@ -395,6 +617,11 @@
nss_top_main.nss[1].load = (uint32_t)load1;
/*
+ * Register sysctl table.
+ */
+ nss_dev_header = register_sysctl_table(nss_root);
+
+ /*
* Register platform_driver
*/
return platform_driver_register(&nss_driver);
@@ -407,6 +634,10 @@
static void __exit nss_cleanup(void)
{
nss_info("Exit NSS driver");
+
+ if (nss_dev_header)
+ unregister_sysctl_table(nss_dev_header);
+
platform_driver_unregister(&nss_driver);
}
diff --git a/nss_tx_rx.c b/nss_tx_rx.c
index ef4370e..afc769b 100755
--- a/nss_tx_rx.c
+++ b/nss_tx_rx.c
@@ -13,6 +13,10 @@
* Global variables/extern declarations
*/
extern struct nss_top_instance nss_top_main;
+extern struct nss_frequency_statistics nss_freq_stat;
+
+#define NSS_ACK_STARTED 0
+#define NSS_ACK_FINISHED 1
#if (NSS_DEBUG_LEVEL > 0)
#define NSS_VERIFY_CTX_MAGIC(x) nss_verify_ctx_magic(x)
@@ -37,6 +41,37 @@
#endif
/*
+ * nss_rx_metadata_nss_freq_ack()
+ * Handle the nss ack of frequency change.
+ */
+static void nss_rx_metadata_nss_freq_ack(struct nss_ctx_instance *nss_ctx, struct nss_freq_ack *nfa)
+{
+ void *ubicom_na_nss_context = NULL;
+ ubicom_na_nss_context = nss_register_ipv4_mgr(NULL);
+
+ if (nfa->ack_status == NSS_ACK_STARTED) {
+
+ /*
+ * NSS finished start noficiation - HW change clocks and send end notification
+ */
+
+ nss_info("%p: NSS ACK Received: %d - Change HW CLK/Send Finish to NSS\n", nss_ctx, nfa->ack);
+ nss_freq_change(ubicom_na_nss_context, nfa->freq_current, 1);
+
+ } else if (nfa->ack_status == NSS_ACK_FINISHED) {
+
+ /*
+ * NSS finished end notification - Done
+ */
+
+ nss_info("%p: NSS Finish End Notification ACK: %d - Running: %dmhz\n", nss_ctx, nfa->ack, nfa->freq_current);
+
+ } else {
+ nss_info("%p: NSS had an error - Running: %dmhz\n", nss_ctx, nfa->freq_current);
+ }
+}
+
+/*
* nss_rx_metadata_ipv4_rule_establish()
* Handle the establishment of an IPv4 rule.
*/
@@ -231,6 +266,52 @@
}
/*
+ * nss_freq_change()
+ * NSS frequency change API
+ */
+nss_tx_status_t nss_freq_change(void *ctx, uint32_t eng, uint32_t start_or_end)
+{
+ struct nss_ctx_instance *nss_ctx = (struct nss_ctx_instance *) ctx;
+ struct sk_buff *nbuf;
+ int32_t status;
+ struct nss_tx_metadata_object *ntmo;
+ struct nss_freq_change *nfc;
+
+ nss_info("%p: Frequency Changing to: %d\n", nss_ctx, eng);
+
+ NSS_VERIFY_CTX_MAGIC(nss_ctx);
+ if (unlikely(nss_ctx->state != NSS_CORE_STATE_INITIALIZED)) {
+ return NSS_TX_FAILURE_NOT_READY;
+ }
+
+ nbuf = __dev_alloc_skb(NSS_NBUF_PAYLOAD_SIZE, GFP_ATOMIC | __GFP_NOWARN);
+ if (unlikely(!nbuf)) {
+ spin_lock_bh(&nss_ctx->nss_top->stats_lock);
+ nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_NBUF_ALLOC_FAILS]++;
+ spin_unlock_bh(&nss_ctx->nss_top->stats_lock);
+ return NSS_TX_FAILURE;
+ }
+
+ ntmo = (struct nss_tx_metadata_object *)skb_put(nbuf, sizeof(struct nss_tx_metadata_object));
+ ntmo->type = NSS_TX_METADATA_TYPE_NSS_FREQ_CHANGE;
+
+ nfc = &ntmo->sub.freq_change;
+ nfc->frequency = eng;
+ nfc->start_or_end = start_or_end;
+
+ status = nss_core_send_buffer(nss_ctx, 0, nbuf, NSS_IF_CMD_QUEUE, H2N_BUFFER_CTRL, 0);
+ if (status != NSS_CORE_STATUS_SUCCESS) {
+ dev_kfree_skb_any(nbuf);
+ nss_info("%p: unable to enqueue 'nss frequency change' - marked as stopped\n", nss_ctx);
+ return NSS_TX_FAILURE;
+ }
+
+ nss_hal_send_interrupt(nss_ctx->nmap, nss_ctx->h2n_desc_rings[NSS_IF_CMD_QUEUE].desc_ring.int_bit, NSS_REGS_H2N_INTR_STATUS_DATA_COMMAND_QUEUE);
+
+ return NSS_TX_SUCCESS;
+}
+
+/*
* nss_rx_metadata_gmac_stats_sync()
* Handle the syncing of GMAC stats.
*/
@@ -464,6 +545,10 @@
nss_rx_metadata_profiler_sync(nss_ctx, &nrmo->sub.profiler_sync);
break;
+ case NSS_RX_METADATA_TYPE_FREQ_ACK:
+ nss_rx_metadata_nss_freq_ack(nss_ctx, &nrmo->sub.freq_ack);
+ break;
+
default:
/*
* WARN: Unknown metadata type