summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/acpica/acapps.h8
-rw-r--r--drivers/acpi/acpica/acglobal.h5
-rw-r--r--drivers/acpi/acpica/aclocal.h2
-rw-r--r--drivers/acpi/acpica/acmacros.h13
-rw-r--r--drivers/acpi/acpica/acopcode.h2
-rw-r--r--drivers/acpi/acpica/acresrc.h6
-rw-r--r--drivers/acpi/acpica/acstruct.h5
-rw-r--r--drivers/acpi/acpica/actables.h9
-rw-r--r--drivers/acpi/acpica/acutils.h22
-rw-r--r--drivers/acpi/acpica/amlcode.h2
-rw-r--r--drivers/acpi/acpica/dsopcode.c7
-rw-r--r--drivers/acpi/acpica/dsutils.c11
-rw-r--r--drivers/acpi/acpica/evgpe.c5
-rw-r--r--drivers/acpi/acpica/evregion.c2
-rw-r--r--drivers/acpi/acpica/evxfevnt.c5
-rw-r--r--drivers/acpi/acpica/exdump.c4
-rw-r--r--drivers/acpi/acpica/exfldio.c10
-rw-r--r--drivers/acpi/acpica/exoparg3.c13
-rw-r--r--drivers/acpi/acpica/exregion.c17
-rw-r--r--drivers/acpi/acpica/hwgpe.c24
-rw-r--r--drivers/acpi/acpica/hwvalid.c16
-rw-r--r--drivers/acpi/acpica/nsdump.c12
-rw-r--r--drivers/acpi/acpica/psopcode.c8
-rw-r--r--drivers/acpi/acpica/psopinfo.c2
-rw-r--r--drivers/acpi/acpica/rsdump.c227
-rw-r--r--drivers/acpi/acpica/tbdata.c35
-rw-r--r--drivers/acpi/acpica/tbinstal.c67
-rw-r--r--drivers/acpi/acpica/tbprint.c19
-rw-r--r--drivers/acpi/acpica/tbxfroot.c7
-rw-r--r--drivers/acpi/acpica/utaddress.c34
-rw-r--r--drivers/acpi/acpica/utbuffer.c8
-rw-r--r--drivers/acpi/acpica/utglobal.c13
-rw-r--r--drivers/acpi/acpica/utmisc.c2
-rw-r--r--drivers/acpi/acpica/utosi.c1
-rw-r--r--drivers/acpi/acpica/utprint.c13
-rw-r--r--drivers/acpi/acpica/utstate.c34
-rw-r--r--drivers/acpi/acpica/utuuid.c2
-rw-r--r--drivers/bcma/driver_mips.c2
-rw-r--r--drivers/bus/Kconfig13
-rw-r--r--drivers/bus/Makefile1
-rw-r--r--drivers/bus/mips_cdmm.c716
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/pistachio/Makefile3
-rw-r--r--drivers/clk/pistachio/clk-pistachio.c329
-rw-r--r--drivers/clk/pistachio/clk-pll.c401
-rw-r--r--drivers/clk/pistachio/clk.c140
-rw-r--r--drivers/clk/pistachio/clk.h174
-rw-r--r--drivers/clocksource/Kconfig2
-rw-r--r--drivers/clocksource/mips-gic-timer.c13
-rw-r--r--drivers/infiniband/hw/cxgb4/mem.c2
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c5
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_vlan.c3
-rw-r--r--drivers/irqchip/Kconfig5
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-bcm7038-l1.c335
-rw-r--r--drivers/irqchip/irq-bcm7120-l2.c193
-rw-r--r--drivers/irqchip/irq-brcmstb-l2.c9
-rw-r--r--drivers/irqchip/irq-mips-gic.c40
-rw-r--r--drivers/net/dsa/mv88e6xxx.c8
-rw-r--r--drivers/net/ethernet/altera/altera_tse_main.c9
-rw-r--r--drivers/net/ethernet/broadcom/b44.c2
-rw-r--r--drivers/net/ethernet/broadcom/bgmac.c2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x.h137
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c9
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c15
-rw-r--r--drivers/net/ethernet/hisilicon/hip04_eth.c18
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_common.c65
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_dcb.c6
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_debugfs.c45
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_ethtool.c29
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_main.c16
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_nvm.c3
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_prototype.h5
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_type.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c244
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_type.h1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf.h1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_main.c9
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c42
-rw-r--r--drivers/net/ethernet/qualcomm/qca_spi.c1
-rw-r--r--drivers/net/ethernet/rocker/rocker.c1
-rw-r--r--drivers/net/ethernet/sfc/selftest.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/common.h5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000.h51
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c26
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c22
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c4
-rw-r--r--drivers/net/ifb.c2
-rw-r--r--drivers/net/macvtap.c2
-rw-r--r--drivers/net/xen-netfront.c2
-rw-r--r--drivers/platform/Kconfig3
-rw-r--r--drivers/platform/Makefile1
-rw-r--r--drivers/platform/mips/Kconfig30
-rw-r--r--drivers/platform/mips/Makefile2
-rw-r--r--drivers/platform/mips/acpi_init.c150
-rw-r--r--drivers/platform/mips/cpu_hwmon.c207
-rw-r--r--drivers/ssb/Kconfig1
-rw-r--r--drivers/ssb/driver_chipcommon_pmu.c2
-rw-r--r--drivers/ssb/driver_mipscore.c2
-rw-r--r--drivers/staging/octeon/ethernet-tx.c5
-rw-r--r--drivers/staging/octeon/ethernet.c10
-rw-r--r--drivers/tty/Kconfig47
-rw-r--r--drivers/tty/Makefile1
-rw-r--r--drivers/tty/mips_ejtag_fdc.c1303
106 files changed, 4904 insertions, 712 deletions
diff --git a/drivers/acpi/acpica/acapps.h b/drivers/acpi/acpica/acapps.h
index d863016565b5..e9f0833e818d 100644
--- a/drivers/acpi/acpica/acapps.h
+++ b/drivers/acpi/acpica/acapps.h
@@ -64,15 +64,15 @@
/* Macros for signons and file headers */
#define ACPI_COMMON_SIGNON(utility_name) \
- "\n%s\n%s version %8.8X%s [%s]\n%s\n\n", \
+ "\n%s\n%s version %8.8X%s\n%s\n\n", \
ACPICA_NAME, \
- utility_name, ((u32) ACPI_CA_VERSION), ACPI_WIDTH, __DATE__, \
+ utility_name, ((u32) ACPI_CA_VERSION), ACPI_WIDTH, \
ACPICA_COPYRIGHT
#define ACPI_COMMON_HEADER(utility_name, prefix) \
- "%s%s\n%s%s version %8.8X%s [%s]\n%s%s\n%s\n", \
+ "%s%s\n%s%s version %8.8X%s\n%s%s\n%s\n", \
prefix, ACPICA_NAME, \
- prefix, utility_name, ((u32) ACPI_CA_VERSION), ACPI_WIDTH, __DATE__, \
+ prefix, utility_name, ((u32) ACPI_CA_VERSION), ACPI_WIDTH, \
prefix, ACPICA_COPYRIGHT, \
prefix
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h
index a165d25343e8..a0c478784314 100644
--- a/drivers/acpi/acpica/acglobal.h
+++ b/drivers/acpi/acpica/acglobal.h
@@ -306,6 +306,7 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_db_output_flags, ACPI_DB_CONSOLE_OUTPUT);
ACPI_INIT_GLOBAL(u8, acpi_gbl_no_resource_disassembly, FALSE);
ACPI_INIT_GLOBAL(u8, acpi_gbl_ignore_noop_operator, FALSE);
ACPI_INIT_GLOBAL(u8, acpi_gbl_cstyle_disassembly, TRUE);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_force_aml_disassembly, FALSE);
ACPI_GLOBAL(u8, acpi_gbl_db_opt_disasm);
ACPI_GLOBAL(u8, acpi_gbl_db_opt_verbose);
@@ -321,9 +322,7 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_db_terminate_threads, FALSE);
ACPI_INIT_GLOBAL(u8, acpi_gbl_abort_method, FALSE);
ACPI_INIT_GLOBAL(u8, acpi_gbl_method_executing, FALSE);
-ACPI_GLOBAL(u8, acpi_gbl_db_opt_tables);
-ACPI_GLOBAL(u8, acpi_gbl_db_opt_stats);
-ACPI_GLOBAL(u8, acpi_gbl_db_opt_ini_methods);
+ACPI_GLOBAL(u8, acpi_gbl_db_opt_no_ini_methods);
ACPI_GLOBAL(u8, acpi_gbl_db_opt_no_region_support);
ACPI_GLOBAL(u8, acpi_gbl_db_output_to_file);
ACPI_GLOBAL(char *, acpi_gbl_db_buffer);
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h
index 7add32e5d8c5..87b27521fcac 100644
--- a/drivers/acpi/acpica/aclocal.h
+++ b/drivers/acpi/acpica/aclocal.h
@@ -53,7 +53,7 @@ typedef u32 acpi_mutex_handle;
/* Total number of aml opcodes defined */
-#define AML_NUM_OPCODES 0x81
+#define AML_NUM_OPCODES 0x82
/* Forward declarations */
diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h
index cf607fe69dbd..c240bdf824f2 100644
--- a/drivers/acpi/acpica/acmacros.h
+++ b/drivers/acpi/acpica/acmacros.h
@@ -63,23 +63,12 @@
#define ACPI_SET64(ptr, val) (*ACPI_CAST64 (ptr) = (u64) (val))
/*
- * printf() format helpers. These macros are workarounds for the difficulties
+ * printf() format helper. This macros is a workaround for the difficulties
* with emitting 64-bit integers and 64-bit pointers with the same code
* for both 32-bit and 64-bit hosts.
*/
#define ACPI_FORMAT_UINT64(i) ACPI_HIDWORD(i), ACPI_LODWORD(i)
-#if ACPI_MACHINE_WIDTH == 64
-#define ACPI_FORMAT_NATIVE_UINT(i) ACPI_FORMAT_UINT64(i)
-#define ACPI_FORMAT_TO_UINT(i) ACPI_FORMAT_UINT64(i)
-#define ACPI_PRINTF_UINT "0x%8.8X%8.8X"
-
-#else
-#define ACPI_FORMAT_NATIVE_UINT(i) 0, (u32) (i)
-#define ACPI_FORMAT_TO_UINT(i) (u32) (i)
-#define ACPI_PRINTF_UINT "0x%8.8X"
-#endif
-
/*
* Macros for moving data around to/from buffers that are possibly unaligned.
* If the hardware supports the transfer of unaligned data, just do the store.
diff --git a/drivers/acpi/acpica/acopcode.h b/drivers/acpi/acpica/acopcode.h
index a5f17de45ac6..fd85ad05a24a 100644
--- a/drivers/acpi/acpica/acopcode.h
+++ b/drivers/acpi/acpica/acopcode.h
@@ -111,6 +111,7 @@
#define ARGP_DWORD_OP ARGP_LIST1 (ARGP_DWORDDATA)
#define ARGP_ELSE_OP ARGP_LIST2 (ARGP_PKGLENGTH, ARGP_TERMLIST)
#define ARGP_EVENT_OP ARGP_LIST1 (ARGP_NAME)
+#define ARGP_EXTERNAL_OP ARGP_LIST3 (ARGP_NAMESTRING, ARGP_BYTEDATA, ARGP_BYTEDATA)
#define ARGP_FATAL_OP ARGP_LIST3 (ARGP_BYTEDATA, ARGP_DWORDDATA, ARGP_TERMARG)
#define ARGP_FIELD_OP ARGP_LIST4 (ARGP_PKGLENGTH, ARGP_NAMESTRING, ARGP_BYTEDATA, ARGP_FIELDLIST)
#define ARGP_FIND_SET_LEFT_BIT_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET)
@@ -243,6 +244,7 @@
#define ARGI_DWORD_OP ARGI_INVALID_OPCODE
#define ARGI_ELSE_OP ARGI_INVALID_OPCODE
#define ARGI_EVENT_OP ARGI_INVALID_OPCODE
+#define ARGI_EXTERNAL_OP ARGI_LIST3 (ARGI_STRING, ARGI_INTEGER, ARGI_INTEGER)
#define ARGI_FATAL_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_INTEGER)
#define ARGI_FIELD_OP ARGI_INVALID_OPCODE
#define ARGI_FIND_SET_LEFT_BIT_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_TARGETREF)
diff --git a/drivers/acpi/acpica/acresrc.h b/drivers/acpi/acpica/acresrc.h
index efc4c7124ccc..6357efb01b93 100644
--- a/drivers/acpi/acpica/acresrc.h
+++ b/drivers/acpi/acpica/acresrc.h
@@ -299,11 +299,13 @@ acpi_rs_set_resource_length(acpi_rsdesc_size total_length,
union aml_resource *aml);
/*
- * rsdump
+ * rsdump - Debugger support
*/
+#ifdef ACPI_DEBUGGER
void acpi_rs_dump_resource_list(struct acpi_resource *resource);
-void acpi_rs_dump_irq_list(u8 * route_table);
+void acpi_rs_dump_irq_list(u8 *route_table);
+#endif
/*
* Resource conversion tables
diff --git a/drivers/acpi/acpica/acstruct.h b/drivers/acpi/acpica/acstruct.h
index d14b547b7cd5..87c7860b3394 100644
--- a/drivers/acpi/acpica/acstruct.h
+++ b/drivers/acpi/acpica/acstruct.h
@@ -68,11 +68,6 @@
#define ACPI_WALK_METHOD 0x01
#define ACPI_WALK_METHOD_RESTART 0x02
-/* Flags for iASL compiler only */
-
-#define ACPI_WALK_CONST_REQUIRED 0x10
-#define ACPI_WALK_CONST_OPTIONAL 0x20
-
struct acpi_walk_state {
struct acpi_walk_state *next; /* Next walk_state in list */
u8 descriptor_type; /* To differentiate various internal objs */
diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h
index 1c127a43017b..7e0b6f1bec9c 100644
--- a/drivers/acpi/acpica/actables.h
+++ b/drivers/acpi/acpica/actables.h
@@ -58,7 +58,9 @@ u8 *acpi_tb_scan_memory_for_rsdp(u8 *start_address, u32 length);
/*
* tbdata - table data structure management
*/
-acpi_status acpi_tb_get_next_root_index(u32 *table_index);
+acpi_status
+acpi_tb_get_next_table_descriptor(u32 *table_index,
+ struct acpi_table_desc **table_desc);
void
acpi_tb_init_table_descriptor(struct acpi_table_desc *table_desc,
@@ -119,11 +121,6 @@ acpi_tb_install_standard_table(acpi_physical_address address,
u8 flags,
u8 reload, u8 override, u32 *table_index);
-acpi_status
-acpi_tb_store_table(acpi_physical_address address,
- struct acpi_table_header *table,
- u32 length, u8 flags, u32 *table_index);
-
void acpi_tb_uninstall_table(struct acpi_table_desc *table_desc);
void acpi_tb_terminate(void);
diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h
index c2f03e8774ad..2b3c5bd222f1 100644
--- a/drivers/acpi/acpica/acutils.h
+++ b/drivers/acpi/acpica/acutils.h
@@ -502,6 +502,9 @@ const union acpi_predefined_info *acpi_ut_get_next_predefined_method(const union
const union acpi_predefined_info *acpi_ut_match_predefined_method(char *name);
+void acpi_ut_get_expected_return_types(char *buffer, u32 expected_btypes);
+
+#if (defined ACPI_ASL_COMPILER || defined ACPI_HELP_APP)
const union acpi_predefined_info *acpi_ut_match_resource_name(char *name);
void
@@ -509,9 +512,8 @@ acpi_ut_display_predefined_method(char *buffer,
const union acpi_predefined_info *this_name,
u8 multi_line);
-void acpi_ut_get_expected_return_types(char *buffer, u32 expected_btypes);
-
u32 acpi_ut_get_resource_bit_width(char *buffer, u16 types);
+#endif
/*
* utstate - Generic state creation/cache routines
@@ -539,14 +541,6 @@ acpi_ut_create_update_state_and_push(union acpi_operand_object *object,
u16 action,
union acpi_generic_state **state_list);
-#ifdef ACPI_FUTURE_USAGE
-acpi_status
-acpi_ut_create_pkg_state_and_push(void *internal_object,
- void *external_object,
- u16 index,
- union acpi_generic_state **state_list);
-#endif /* ACPI_FUTURE_USAGE */
-
union acpi_generic_state *acpi_ut_create_control_state(void);
void acpi_ut_delete_generic_state(union acpi_generic_state *state);
@@ -570,7 +564,9 @@ const struct acpi_exception_info *acpi_ut_validate_exception(acpi_status
u8 acpi_ut_is_pci_root_bridge(char *id);
+#if (defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP)
u8 acpi_ut_is_aml_table(struct acpi_table_header *table);
+#endif
acpi_status
acpi_ut_walk_package_tree(union acpi_operand_object *source_object,
@@ -629,15 +625,19 @@ acpi_ut_get_resource_end_tag(union acpi_operand_object *obj_desc, u8 **end_tag);
*/
void acpi_ut_strupr(char *src_string);
+#ifdef ACPI_ASL_COMPILER
void acpi_ut_strlwr(char *src_string);
int acpi_ut_stricmp(char *string1, char *string2);
+#endif
acpi_status acpi_ut_strtoul64(char *string, u32 base, u64 *ret_integer);
void acpi_ut_print_string(char *string, u16 max_length);
+#if defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP
void ut_convert_backslashes(char *pathname);
+#endif
u8 acpi_ut_valid_acpi_name(char *name);
@@ -785,6 +785,8 @@ int acpi_ut_file_printf(ACPI_FILE file, const char *format, ...);
/*
* utuuid -- UUID support functions
*/
+#if (defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP || defined ACPI_HELP_APP)
void acpi_ut_convert_string_to_uuid(char *in_string, u8 *uuid_buffer);
+#endif
#endif /* _ACUTILS_H */
diff --git a/drivers/acpi/acpica/amlcode.h b/drivers/acpi/acpica/amlcode.h
index 3a95068fc119..be9fd009cb28 100644
--- a/drivers/acpi/acpica/amlcode.h
+++ b/drivers/acpi/acpica/amlcode.h
@@ -65,6 +65,7 @@
#define AML_PACKAGE_OP (u16) 0x12
#define AML_VAR_PACKAGE_OP (u16) 0x13 /* ACPI 2.0 */
#define AML_METHOD_OP (u16) 0x14
+#define AML_EXTERNAL_OP (u16) 0x15 /* ACPI 6.0 */
#define AML_DUAL_NAME_PREFIX (u16) 0x2e
#define AML_MULTI_NAME_PREFIX_OP (u16) 0x2f
#define AML_NAME_CHAR_SUBSEQ (u16) 0x30
@@ -206,7 +207,6 @@
#define AML_INT_RESERVEDFIELD_OP (u16) 0x0031
#define AML_INT_ACCESSFIELD_OP (u16) 0x0032
#define AML_INT_BYTELIST_OP (u16) 0x0033
-#define AML_INT_STATICSTRING_OP (u16) 0x0034
#define AML_INT_METHODCALL_OP (u16) 0x0035
#define AML_INT_RETURN_VALUE_OP (u16) 0x0036
#define AML_INT_EVAL_SUBTREE_OP (u16) 0x0037
diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c
index 77244182ff02..ea0cc4e08f80 100644
--- a/drivers/acpi/acpica/dsopcode.c
+++ b/drivers/acpi/acpica/dsopcode.c
@@ -446,7 +446,7 @@ acpi_ds_eval_region_operands(struct acpi_walk_state *walk_state,
ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "RgnObj %p Addr %8.8X%8.8X Len %X\n",
obj_desc,
- ACPI_FORMAT_NATIVE_UINT(obj_desc->region.address),
+ ACPI_FORMAT_UINT64(obj_desc->region.address),
obj_desc->region.length));
/* Now the address and length are valid for this opregion */
@@ -539,13 +539,12 @@ acpi_ds_eval_table_region_operands(struct acpi_walk_state *walk_state,
return_ACPI_STATUS(AE_NOT_EXIST);
}
- obj_desc->region.address =
- (acpi_physical_address) ACPI_TO_INTEGER(table);
+ obj_desc->region.address = ACPI_PTR_TO_PHYSADDR(table);
obj_desc->region.length = table->length;
ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "RgnObj %p Addr %8.8X%8.8X Len %X\n",
obj_desc,
- ACPI_FORMAT_NATIVE_UINT(obj_desc->region.address),
+ ACPI_FORMAT_UINT64(obj_desc->region.address),
obj_desc->region.length));
/* Now the address and length are valid for this opregion */
diff --git a/drivers/acpi/acpica/dsutils.c b/drivers/acpi/acpica/dsutils.c
index e5ff89bcb3f5..deeddd6d2f05 100644
--- a/drivers/acpi/acpica/dsutils.c
+++ b/drivers/acpi/acpica/dsutils.c
@@ -564,6 +564,17 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
acpi_operand_object,
acpi_gbl_root_node);
status = AE_OK;
+ } else if (parent_op->common.aml_opcode ==
+ AML_EXTERNAL_OP) {
+
+ /* TBD: May only be temporary */
+
+ obj_desc =
+ acpi_ut_create_string_object((acpi_size) name_length);
+
+ ACPI_STRNCPY(obj_desc->string.pointer,
+ name_string, name_length);
+ status = AE_OK;
} else {
/*
* We just plain didn't find it -- which is a
diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c
index 5ed064e8673c..ccf793247447 100644
--- a/drivers/acpi/acpica/evgpe.c
+++ b/drivers/acpi/acpica/evgpe.c
@@ -92,6 +92,7 @@ acpi_ev_update_gpe_enable_mask(struct acpi_gpe_event_info *gpe_event_info)
ACPI_SET_BIT(gpe_register_info->enable_for_run,
(u8)register_bit);
}
+ gpe_register_info->enable_mask = gpe_register_info->enable_for_run;
return_ACPI_STATUS(AE_OK);
}
@@ -123,7 +124,7 @@ acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info)
/* Enable the requested GPE */
- status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE_SAVE);
+ status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE);
return_ACPI_STATUS(status);
}
@@ -202,7 +203,7 @@ acpi_ev_remove_gpe_reference(struct acpi_gpe_event_info *gpe_event_info)
if (ACPI_SUCCESS(status)) {
status =
acpi_hw_low_set_gpe(gpe_event_info,
- ACPI_GPE_DISABLE_SAVE);
+ ACPI_GPE_DISABLE);
}
if (ACPI_FAILURE(status)) {
diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c
index 9abace3401f9..2ba28a63fb68 100644
--- a/drivers/acpi/acpica/evregion.c
+++ b/drivers/acpi/acpica/evregion.c
@@ -272,7 +272,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
"Handler %p (@%p) Address %8.8X%8.8X [%s]\n",
&region_obj->region.handler->address_space, handler,
- ACPI_FORMAT_NATIVE_UINT(address),
+ ACPI_FORMAT_UINT64(address),
acpi_ut_get_region_name(region_obj->region.
space_id)));
diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c
index df06a23c4197..faad911d46b5 100644
--- a/drivers/acpi/acpica/evxfevnt.c
+++ b/drivers/acpi/acpica/evxfevnt.c
@@ -356,7 +356,8 @@ acpi_status acpi_get_event_status(u32 event, acpi_event_status * event_status)
}
if (in_byte) {
- local_event_status |= ACPI_EVENT_FLAG_ENABLED;
+ local_event_status |=
+ (ACPI_EVENT_FLAG_ENABLED | ACPI_EVENT_FLAG_ENABLE_SET);
}
/* Fixed event currently active? */
@@ -369,7 +370,7 @@ acpi_status acpi_get_event_status(u32 event, acpi_event_status * event_status)
}
if (in_byte) {
- local_event_status |= ACPI_EVENT_FLAG_SET;
+ local_event_status |= ACPI_EVENT_FLAG_STATUS_SET;
}
(*event_status) = local_event_status;
diff --git a/drivers/acpi/acpica/exdump.c b/drivers/acpi/acpica/exdump.c
index 7c213b6b6472..1da52bef632e 100644
--- a/drivers/acpi/acpica/exdump.c
+++ b/drivers/acpi/acpica/exdump.c
@@ -767,8 +767,8 @@ void acpi_ex_dump_operand(union acpi_operand_object *obj_desc, u32 depth)
acpi_os_printf("\n");
} else {
acpi_os_printf(" base %8.8X%8.8X Length %X\n",
- ACPI_FORMAT_NATIVE_UINT(obj_desc->region.
- address),
+ ACPI_FORMAT_UINT64(obj_desc->region.
+ address),
obj_desc->region.length);
}
break;
diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c
index 49479927e7f7..725a3746a2df 100644
--- a/drivers/acpi/acpica/exfldio.c
+++ b/drivers/acpi/acpica/exfldio.c
@@ -263,17 +263,15 @@ acpi_ex_access_region(union acpi_operand_object *obj_desc,
}
ACPI_DEBUG_PRINT_RAW((ACPI_DB_BFIELD,
- " Region [%s:%X], Width %X, ByteBase %X, Offset %X at %p\n",
+ " Region [%s:%X], Width %X, ByteBase %X, Offset %X at %8.8X%8.8X\n",
acpi_ut_get_region_name(rgn_desc->region.
space_id),
rgn_desc->region.space_id,
obj_desc->common_field.access_byte_width,
obj_desc->common_field.base_byte_offset,
- field_datum_byte_offset, ACPI_CAST_PTR(void,
- (rgn_desc->
- region.
- address +
- region_offset))));
+ field_datum_byte_offset,
+ ACPI_FORMAT_UINT64(rgn_desc->region.address +
+ region_offset)));
/* Invoke the appropriate address_space/op_region handler */
diff --git a/drivers/acpi/acpica/exoparg3.c b/drivers/acpi/acpica/exoparg3.c
index b813fed95e56..1c64a988cbee 100644
--- a/drivers/acpi/acpica/exoparg3.c
+++ b/drivers/acpi/acpica/exoparg3.c
@@ -114,7 +114,18 @@ acpi_status acpi_ex_opcode_3A_0T_0R(struct acpi_walk_state *walk_state)
/* Might return while OS is shutting down, just continue */
ACPI_FREE(fatal);
- break;
+ goto cleanup;
+
+ case AML_EXTERNAL_OP:
+ /*
+ * If the interpreter sees this opcode, just ignore it. The External
+ * op is intended for use by disassemblers in order to properly
+ * disassemble control method invocations. The opcode or group of
+ * opcodes should be surrounded by an "if (0)" clause to ensure that
+ * AML interpreters never see the opcode.
+ */
+ status = AE_OK;
+ goto cleanup;
default:
diff --git a/drivers/acpi/acpica/exregion.c b/drivers/acpi/acpica/exregion.c
index 0fe188e238ef..f6c2f5499935 100644
--- a/drivers/acpi/acpica/exregion.c
+++ b/drivers/acpi/acpica/exregion.c
@@ -165,8 +165,8 @@ acpi_ex_system_memory_space_handler(u32 function,
* one page, which is similar to the original code that used a 4k
* maximum window.
*/
- page_boundary_map_length =
- ACPI_ROUND_UP(address, ACPI_DEFAULT_PAGE_SIZE) - address;
+ page_boundary_map_length = (acpi_size)
+ (ACPI_ROUND_UP(address, ACPI_DEFAULT_PAGE_SIZE) - address);
if (page_boundary_map_length == 0) {
page_boundary_map_length = ACPI_DEFAULT_PAGE_SIZE;
}
@@ -177,12 +177,13 @@ acpi_ex_system_memory_space_handler(u32 function,
/* Create a new mapping starting at the address given */
- mem_info->mapped_logical_address = acpi_os_map_memory((acpi_physical_address) address, map_length);
+ mem_info->mapped_logical_address =
+ acpi_os_map_memory(address, map_length);
if (!mem_info->mapped_logical_address) {
ACPI_ERROR((AE_INFO,
"Could not map memory at 0x%8.8X%8.8X, size %u",
- ACPI_FORMAT_NATIVE_UINT(address),
- (u32) map_length));
+ ACPI_FORMAT_UINT64(address),
+ (u32)map_length));
mem_info->mapped_length = 0;
return_ACPI_STATUS(AE_NO_MEMORY);
}
@@ -202,8 +203,7 @@ acpi_ex_system_memory_space_handler(u32 function,
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"System-Memory (width %u) R/W %u Address=%8.8X%8.8X\n",
- bit_width, function,
- ACPI_FORMAT_NATIVE_UINT(address)));
+ bit_width, function, ACPI_FORMAT_UINT64(address)));
/*
* Perform the memory read or write
@@ -318,8 +318,7 @@ acpi_ex_system_io_space_handler(u32 function,
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"System-IO (width %u) R/W %u Address=%8.8X%8.8X\n",
- bit_width, function,
- ACPI_FORMAT_NATIVE_UINT(address)));
+ bit_width, function, ACPI_FORMAT_UINT64(address)));
/* Decode the function parameter */
diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c
index 84bc550f4f1d..73cfa5947ff3 100644
--- a/drivers/acpi/acpica/hwgpe.c
+++ b/drivers/acpi/acpica/hwgpe.c
@@ -89,6 +89,8 @@ u32 acpi_hw_get_gpe_register_bit(struct acpi_gpe_event_info *gpe_event_info)
* RETURN: Status
*
* DESCRIPTION: Enable or disable a single GPE in the parent enable register.
+ * The enable_mask field of the involved GPE register must be
+ * updated by the caller if necessary.
*
******************************************************************************/
@@ -119,7 +121,7 @@ acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action)
/* Set or clear just the bit that corresponds to this GPE */
register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info);
- switch (action & ~ACPI_GPE_SAVE_MASK) {
+ switch (action) {
case ACPI_GPE_CONDITIONAL_ENABLE:
/* Only enable if the corresponding enable_mask bit is set */
@@ -149,9 +151,6 @@ acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action)
/* Write the updated enable mask */
status = acpi_hw_write(enable_mask, &gpe_register_info->enable_address);
- if (ACPI_SUCCESS(status) && (action & ACPI_GPE_SAVE_MASK)) {
- gpe_register_info->enable_mask = (u8)enable_mask;
- }
return (status);
}
@@ -250,6 +249,17 @@ acpi_hw_get_gpe_status(struct acpi_gpe_event_info * gpe_event_info,
local_event_status |= ACPI_EVENT_FLAG_WAKE_ENABLED;
}
+ /* GPE currently enabled (enable bit == 1)? */
+
+ status = acpi_hw_read(&in_byte, &gpe_register_info->enable_address);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ if (register_bit & in_byte) {
+ local_event_status |= ACPI_EVENT_FLAG_ENABLE_SET;
+ }
+
/* GPE currently active (status bit == 1)? */
status = acpi_hw_read(&in_byte, &gpe_register_info->status_address);
@@ -258,7 +268,7 @@ acpi_hw_get_gpe_status(struct acpi_gpe_event_info * gpe_event_info,
}
if (register_bit & in_byte) {
- local_event_status |= ACPI_EVENT_FLAG_SET;
+ local_event_status |= ACPI_EVENT_FLAG_STATUS_SET;
}
/* Set return value */
@@ -286,10 +296,8 @@ acpi_hw_gpe_enable_write(u8 enable_mask,
{
acpi_status status;
+ gpe_register_info->enable_mask = enable_mask;
status = acpi_hw_write(enable_mask, &gpe_register_info->enable_address);
- if (ACPI_SUCCESS(status)) {
- gpe_register_info->enable_mask = enable_mask;
- }
return (status);
}
diff --git a/drivers/acpi/acpica/hwvalid.c b/drivers/acpi/acpica/hwvalid.c
index 2bd33fe56cb3..29033d71417b 100644
--- a/drivers/acpi/acpica/hwvalid.c
+++ b/drivers/acpi/acpica/hwvalid.c
@@ -142,17 +142,17 @@ acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width)
byte_width = ACPI_DIV_8(bit_width);
last_address = address + byte_width - 1;
- ACPI_DEBUG_PRINT((ACPI_DB_IO, "Address %p LastAddress %p Length %X",
- ACPI_CAST_PTR(void, address), ACPI_CAST_PTR(void,
- last_address),
- byte_width));
+ ACPI_DEBUG_PRINT((ACPI_DB_IO,
+ "Address %8.8X%8.8X LastAddress %8.8X%8.8X Length %X",
+ ACPI_FORMAT_UINT64(address),
+ ACPI_FORMAT_UINT64(last_address), byte_width));
/* Maximum 16-bit address in I/O space */
if (last_address > ACPI_UINT16_MAX) {
ACPI_ERROR((AE_INFO,
- "Illegal I/O port address/length above 64K: %p/0x%X",
- ACPI_CAST_PTR(void, address), byte_width));
+ "Illegal I/O port address/length above 64K: %8.8X%8.8X/0x%X",
+ ACPI_FORMAT_UINT64(address), byte_width));
return_ACPI_STATUS(AE_LIMIT);
}
@@ -181,8 +181,8 @@ acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width)
if (acpi_gbl_osi_data >= port_info->osi_dependency) {
ACPI_DEBUG_PRINT((ACPI_DB_IO,
- "Denied AML access to port 0x%p/%X (%s 0x%.4X-0x%.4X)",
- ACPI_CAST_PTR(void, address),
+ "Denied AML access to port 0x%8.8X%8.8X/%X (%s 0x%.4X-0x%.4X)",
+ ACPI_FORMAT_UINT64(address),
byte_width, port_info->name,
port_info->start,
port_info->end));
diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c
index 80f097eb7381..d259393505fa 100644
--- a/drivers/acpi/acpica/nsdump.c
+++ b/drivers/acpi/acpica/nsdump.c
@@ -271,12 +271,11 @@ acpi_ns_dump_one_object(acpi_handle obj_handle,
switch (type) {
case ACPI_TYPE_PROCESSOR:
- acpi_os_printf("ID %02X Len %02X Addr %p\n",
+ acpi_os_printf("ID %02X Len %02X Addr %8.8X%8.8X\n",
obj_desc->processor.proc_id,
obj_desc->processor.length,
- ACPI_CAST_PTR(void,
- obj_desc->processor.
- address));
+ ACPI_FORMAT_UINT64(obj_desc->processor.
+ address));
break;
case ACPI_TYPE_DEVICE:
@@ -347,8 +346,9 @@ acpi_ns_dump_one_object(acpi_handle obj_handle,
space_id));
if (obj_desc->region.flags & AOPOBJ_DATA_VALID) {
acpi_os_printf(" Addr %8.8X%8.8X Len %.4X\n",
- ACPI_FORMAT_NATIVE_UINT
- (obj_desc->region.address),
+ ACPI_FORMAT_UINT64(obj_desc->
+ region.
+ address),
obj_desc->region.length);
} else {
acpi_os_printf
diff --git a/drivers/acpi/acpica/psopcode.c b/drivers/acpi/acpica/psopcode.c
index 1af4a405e351..ed90fddf2487 100644
--- a/drivers/acpi/acpica/psopcode.c
+++ b/drivers/acpi/acpica/psopcode.c
@@ -646,7 +646,13 @@ const struct acpi_opcode_info acpi_gbl_aml_op_info[AML_NUM_OPCODES] = {
AML_CLASS_INTERNAL, AML_TYPE_BOGUS, AML_HAS_ARGS),
/* 80 */ ACPI_OP("-ExtAccessField-", ARGP_CONNECTFIELD_OP,
ARGI_CONNECTFIELD_OP, ACPI_TYPE_ANY,
- AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0)
+ AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0),
+
+/* ACPI 6.0 opcodes */
+
+ /* 81 */ ACPI_OP("External", ARGP_EXTERNAL_OP, ARGI_EXTERNAL_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE, /* ? */
+ AML_TYPE_EXEC_3A_0T_0R, AML_FLAGS_EXEC_3A_0T_0R)
/*! [End] no source code translation !*/
};
diff --git a/drivers/acpi/acpica/psopinfo.c b/drivers/acpi/acpica/psopinfo.c
index e18e7c47f482..20e1a35169fc 100644
--- a/drivers/acpi/acpica/psopinfo.c
+++ b/drivers/acpi/acpica/psopinfo.c
@@ -210,7 +210,7 @@ const u8 acpi_gbl_short_op_index[256] = {
/* 8 9 A B C D E F */
/* 0x00 */ 0x00, 0x01, _UNK, _UNK, _UNK, _UNK, 0x02, _UNK,
/* 0x08 */ 0x03, _UNK, 0x04, 0x05, 0x06, 0x07, 0x6E, _UNK,
-/* 0x10 */ 0x08, 0x09, 0x0a, 0x6F, 0x0b, _UNK, _UNK, _UNK,
+/* 0x10 */ 0x08, 0x09, 0x0a, 0x6F, 0x0b, 0x81, _UNK, _UNK,
/* 0x18 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
/* 0x20 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
/* 0x28 */ _UNK, _UNK, _UNK, _UNK, _UNK, 0x63, _PFX, _PFX,
diff --git a/drivers/acpi/acpica/rsdump.c b/drivers/acpi/acpica/rsdump.c
index 1539394c8c52..c428bb33204e 100644
--- a/drivers/acpi/acpica/rsdump.c
+++ b/drivers/acpi/acpica/rsdump.c
@@ -1,6 +1,6 @@
/*******************************************************************************
*
- * Module Name: rsdump - Functions to display the resource structures.
+ * Module Name: rsdump - AML debugger support for resource structures.
*
******************************************************************************/
@@ -48,7 +48,10 @@
#define _COMPONENT ACPI_RESOURCES
ACPI_MODULE_NAME("rsdump")
-#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DISASSEMBLER) || defined(ACPI_DEBUGGER)
+/*
+ * All functions in this module are used by the AML Debugger only
+ */
+#if defined(ACPI_DEBUGGER)
/* Local prototypes */
static void acpi_rs_out_string(char *title, char *value);
@@ -80,6 +83,116 @@ acpi_rs_dump_descriptor(void *resource, struct acpi_rsdump_info *table);
/*******************************************************************************
*
+ * FUNCTION: acpi_rs_dump_resource_list
+ *
+ * PARAMETERS: resource_list - Pointer to a resource descriptor list
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dispatches the structure to the correct dump routine.
+ *
+ ******************************************************************************/
+
+void acpi_rs_dump_resource_list(struct acpi_resource *resource_list)
+{
+ u32 count = 0;
+ u32 type;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Check if debug output enabled */
+
+ if (!ACPI_IS_DEBUG_ENABLED(ACPI_LV_RESOURCES, _COMPONENT)) {
+ return;
+ }
+
+ /* Walk list and dump all resource descriptors (END_TAG terminates) */
+
+ do {
+ acpi_os_printf("\n[%02X] ", count);
+ count++;
+
+ /* Validate Type before dispatch */
+
+ type = resource_list->type;
+ if (type > ACPI_RESOURCE_TYPE_MAX) {
+ acpi_os_printf
+ ("Invalid descriptor type (%X) in resource list\n",
+ resource_list->type);
+ return;
+ }
+
+ /* Sanity check the length. It must not be zero, or we loop forever */
+
+ if (!resource_list->length) {
+ acpi_os_printf
+ ("Invalid zero length descriptor in resource list\n");
+ return;
+ }
+
+ /* Dump the resource descriptor */
+
+ if (type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
+ acpi_rs_dump_descriptor(&resource_list->data,
+ acpi_gbl_dump_serial_bus_dispatch
+ [resource_list->data.
+ common_serial_bus.type]);
+ } else {
+ acpi_rs_dump_descriptor(&resource_list->data,
+ acpi_gbl_dump_resource_dispatch
+ [type]);
+ }
+
+ /* Point to the next resource structure */
+
+ resource_list = ACPI_NEXT_RESOURCE(resource_list);
+
+ /* Exit when END_TAG descriptor is reached */
+
+ } while (type != ACPI_RESOURCE_TYPE_END_TAG);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_dump_irq_list
+ *
+ * PARAMETERS: route_table - Pointer to the routing table to dump.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print IRQ routing table
+ *
+ ******************************************************************************/
+
+void acpi_rs_dump_irq_list(u8 *route_table)
+{
+ struct acpi_pci_routing_table *prt_element;
+ u8 count;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Check if debug output enabled */
+
+ if (!ACPI_IS_DEBUG_ENABLED(ACPI_LV_RESOURCES, _COMPONENT)) {
+ return;
+ }
+
+ prt_element = ACPI_CAST_PTR(struct acpi_pci_routing_table, route_table);
+
+ /* Dump all table elements, Exit on zero length element */
+
+ for (count = 0; prt_element->length; count++) {
+ acpi_os_printf("\n[%02X] PCI IRQ Routing Table Package\n",
+ count);
+ acpi_rs_dump_descriptor(prt_element, acpi_rs_dump_prt);
+
+ prt_element = ACPI_ADD_PTR(struct acpi_pci_routing_table,
+ prt_element, prt_element->length);
+ }
+}
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_rs_dump_descriptor
*
* PARAMETERS: resource - Buffer containing the resource
@@ -357,116 +470,6 @@ static void acpi_rs_dump_address_common(union acpi_resource_data *resource)
/*******************************************************************************
*
- * FUNCTION: acpi_rs_dump_resource_list
- *
- * PARAMETERS: resource_list - Pointer to a resource descriptor list
- *
- * RETURN: None
- *
- * DESCRIPTION: Dispatches the structure to the correct dump routine.
- *
- ******************************************************************************/
-
-void acpi_rs_dump_resource_list(struct acpi_resource *resource_list)
-{
- u32 count = 0;
- u32 type;
-
- ACPI_FUNCTION_ENTRY();
-
- /* Check if debug output enabled */
-
- if (!ACPI_IS_DEBUG_ENABLED(ACPI_LV_RESOURCES, _COMPONENT)) {
- return;
- }
-
- /* Walk list and dump all resource descriptors (END_TAG terminates) */
-
- do {
- acpi_os_printf("\n[%02X] ", count);
- count++;
-
- /* Validate Type before dispatch */
-
- type = resource_list->type;
- if (type > ACPI_RESOURCE_TYPE_MAX) {
- acpi_os_printf
- ("Invalid descriptor type (%X) in resource list\n",
- resource_list->type);
- return;
- }
-
- /* Sanity check the length. It must not be zero, or we loop forever */
-
- if (!resource_list->length) {
- acpi_os_printf
- ("Invalid zero length descriptor in resource list\n");
- return;
- }
-
- /* Dump the resource descriptor */
-
- if (type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
- acpi_rs_dump_descriptor(&resource_list->data,
- acpi_gbl_dump_serial_bus_dispatch
- [resource_list->data.
- common_serial_bus.type]);
- } else {
- acpi_rs_dump_descriptor(&resource_list->data,
- acpi_gbl_dump_resource_dispatch
- [type]);
- }
-
- /* Point to the next resource structure */
-
- resource_list = ACPI_NEXT_RESOURCE(resource_list);
-
- /* Exit when END_TAG descriptor is reached */
-
- } while (type != ACPI_RESOURCE_TYPE_END_TAG);
-}
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_rs_dump_irq_list
- *
- * PARAMETERS: route_table - Pointer to the routing table to dump.
- *
- * RETURN: None
- *
- * DESCRIPTION: Print IRQ routing table
- *
- ******************************************************************************/
-
-void acpi_rs_dump_irq_list(u8 * route_table)
-{
- struct acpi_pci_routing_table *prt_element;
- u8 count;
-
- ACPI_FUNCTION_ENTRY();
-
- /* Check if debug output enabled */
-
- if (!ACPI_IS_DEBUG_ENABLED(ACPI_LV_RESOURCES, _COMPONENT)) {
- return;
- }
-
- prt_element = ACPI_CAST_PTR(struct acpi_pci_routing_table, route_table);
-
- /* Dump all table elements, Exit on zero length element */
-
- for (count = 0; prt_element->length; count++) {
- acpi_os_printf("\n[%02X] PCI IRQ Routing Table Package\n",
- count);
- acpi_rs_dump_descriptor(prt_element, acpi_rs_dump_prt);
-
- prt_element = ACPI_ADD_PTR(struct acpi_pci_routing_table,
- prt_element, prt_element->length);
- }
-}
-
-/*******************************************************************************
- *
* FUNCTION: acpi_rs_out*
*
* PARAMETERS: title - Name of the resource field
diff --git a/drivers/acpi/acpica/tbdata.c b/drivers/acpi/acpica/tbdata.c
index 6a144957aadd..d7f8386455bd 100644
--- a/drivers/acpi/acpica/tbdata.c
+++ b/drivers/acpi/acpica/tbdata.c
@@ -113,9 +113,9 @@ acpi_tb_acquire_table(struct acpi_table_desc *table_desc,
case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL:
case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL:
- table =
- ACPI_CAST_PTR(struct acpi_table_header,
- table_desc->address);
+ table = ACPI_CAST_PTR(struct acpi_table_header,
+ ACPI_PHYSADDR_TO_PTR(table_desc->
+ address));
break;
default:
@@ -214,7 +214,8 @@ acpi_tb_acquire_temp_table(struct acpi_table_desc *table_desc,
case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL:
case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL:
- table_header = ACPI_CAST_PTR(struct acpi_table_header, address);
+ table_header = ACPI_CAST_PTR(struct acpi_table_header,
+ ACPI_PHYSADDR_TO_PTR(address));
if (!table_header) {
return (AE_NO_MEMORY);
}
@@ -398,14 +399,14 @@ acpi_tb_verify_temp_table(struct acpi_table_desc * table_desc, char *signature)
table_desc->length);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, AE_NO_MEMORY,
- "%4.4s " ACPI_PRINTF_UINT
+ "%4.4s 0x%8.8X%8.8X"
" Attempted table install failed",
acpi_ut_valid_acpi_name(table_desc->
signature.
ascii) ?
table_desc->signature.ascii : "????",
- ACPI_FORMAT_TO_UINT(table_desc->
- address)));
+ ACPI_FORMAT_UINT64(table_desc->
+ address)));
goto invalidate_and_exit;
}
}
@@ -483,19 +484,23 @@ acpi_status acpi_tb_resize_root_table_list(void)
/*******************************************************************************
*
- * FUNCTION: acpi_tb_get_next_root_index
+ * FUNCTION: acpi_tb_get_next_table_descriptor
*
* PARAMETERS: table_index - Where table index is returned
+ * table_desc - Where table descriptor is returned
*
- * RETURN: Status and table index.
+ * RETURN: Status and table index/descriptor.
*
* DESCRIPTION: Allocate a new ACPI table entry to the global table list
*
******************************************************************************/
-acpi_status acpi_tb_get_next_root_index(u32 *table_index)
+acpi_status
+acpi_tb_get_next_table_descriptor(u32 *table_index,
+ struct acpi_table_desc **table_desc)
{
acpi_status status;
+ u32 i;
/* Ensure that there is room for the table in the Root Table List */
@@ -507,8 +512,16 @@ acpi_status acpi_tb_get_next_root_index(u32 *table_index)
}
}
- *table_index = acpi_gbl_root_table_list.current_table_count;
+ i = acpi_gbl_root_table_list.current_table_count;
acpi_gbl_root_table_list.current_table_count++;
+
+ if (table_index) {
+ *table_index = i;
+ }
+ if (table_desc) {
+ *table_desc = &acpi_gbl_root_table_list.tables[i];
+ }
+
return (AE_OK);
}
diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c
index 9bad45e63a45..008a251780f4 100644
--- a/drivers/acpi/acpica/tbinstal.c
+++ b/drivers/acpi/acpica/tbinstal.c
@@ -187,8 +187,9 @@ acpi_tb_install_fixed_table(acpi_physical_address address,
status = acpi_tb_acquire_temp_table(&new_table_desc, address,
ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL);
if (ACPI_FAILURE(status)) {
- ACPI_ERROR((AE_INFO, "Could not acquire table length at %p",
- ACPI_CAST_PTR(void, address)));
+ ACPI_ERROR((AE_INFO,
+ "Could not acquire table length at %8.8X%8.8X",
+ ACPI_FORMAT_UINT64(address)));
return_ACPI_STATUS(status);
}
@@ -246,8 +247,9 @@ acpi_tb_install_standard_table(acpi_physical_address address,
status = acpi_tb_acquire_temp_table(&new_table_desc, address, flags);
if (ACPI_FAILURE(status)) {
- ACPI_ERROR((AE_INFO, "Could not acquire table length at %p",
- ACPI_CAST_PTR(void, address)));
+ ACPI_ERROR((AE_INFO,
+ "Could not acquire table length at %8.8X%8.8X",
+ ACPI_FORMAT_UINT64(address)));
return_ACPI_STATUS(status);
}
@@ -258,9 +260,10 @@ acpi_tb_install_standard_table(acpi_physical_address address,
if (!reload &&
acpi_gbl_disable_ssdt_table_install &&
ACPI_COMPARE_NAME(&new_table_desc.signature, ACPI_SIG_SSDT)) {
- ACPI_INFO((AE_INFO, "Ignoring installation of %4.4s at %p",
- new_table_desc.signature.ascii, ACPI_CAST_PTR(void,
- address)));
+ ACPI_INFO((AE_INFO,
+ "Ignoring installation of %4.4s at %8.8X%8.8X",
+ new_table_desc.signature.ascii,
+ ACPI_FORMAT_UINT64(address)));
goto release_and_exit;
}
@@ -346,7 +349,6 @@ acpi_tb_install_standard_table(acpi_physical_address address,
*/
acpi_tb_uninstall_table(&new_table_desc);
*table_index = i;
- (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
return_ACPI_STATUS(AE_OK);
}
}
@@ -354,7 +356,7 @@ acpi_tb_install_standard_table(acpi_physical_address address,
/* Add the table to the global root table list */
- status = acpi_tb_get_next_root_index(&i);
+ status = acpi_tb_get_next_table_descriptor(&i, NULL);
if (ACPI_FAILURE(status)) {
goto release_and_exit;
}
@@ -429,11 +431,11 @@ finish_override:
return;
}
- ACPI_INFO((AE_INFO, "%4.4s " ACPI_PRINTF_UINT
- " %s table override, new table: " ACPI_PRINTF_UINT,
+ ACPI_INFO((AE_INFO, "%4.4s 0x%8.8X%8.8X"
+ " %s table override, new table: 0x%8.8X%8.8X",
old_table_desc->signature.ascii,
- ACPI_FORMAT_TO_UINT(old_table_desc->address),
- override_type, ACPI_FORMAT_TO_UINT(new_table_desc.address)));
+ ACPI_FORMAT_UINT64(old_table_desc->address),
+ override_type, ACPI_FORMAT_UINT64(new_table_desc.address)));
/* We can now uninstall the original table */
@@ -455,43 +457,6 @@ finish_override:
/*******************************************************************************
*
- * FUNCTION: acpi_tb_store_table
- *
- * PARAMETERS: address - Table address
- * table - Table header
- * length - Table length
- * flags - Install flags
- * table_index - Where the table index is returned
- *
- * RETURN: Status and table index.
- *
- * DESCRIPTION: Add an ACPI table to the global table list
- *
- ******************************************************************************/
-
-acpi_status
-acpi_tb_store_table(acpi_physical_address address,
- struct acpi_table_header * table,
- u32 length, u8 flags, u32 *table_index)
-{
- acpi_status status;
- struct acpi_table_desc *table_desc;
-
- status = acpi_tb_get_next_root_index(table_index);
- if (ACPI_FAILURE(status)) {
- return (status);
- }
-
- /* Initialize added table */
-
- table_desc = &acpi_gbl_root_table_list.tables[*table_index];
- acpi_tb_init_table_descriptor(table_desc, address, flags, table);
- table_desc->pointer = table;
- return (AE_OK);
-}
-
-/*******************************************************************************
- *
* FUNCTION: acpi_tb_uninstall_table
*
* PARAMETERS: table_desc - Table descriptor
@@ -517,7 +482,7 @@ void acpi_tb_uninstall_table(struct acpi_table_desc *table_desc)
if ((table_desc->flags & ACPI_TABLE_ORIGIN_MASK) ==
ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL) {
- ACPI_FREE(ACPI_CAST_PTR(void, table_desc->address));
+ ACPI_FREE(ACPI_PHYSADDR_TO_PTR(table_desc->address));
}
table_desc->address = ACPI_PTR_TO_PHYSADDR(NULL);
diff --git a/drivers/acpi/acpica/tbprint.c b/drivers/acpi/acpica/tbprint.c
index ef16c06e5091..77ba5c71c6e7 100644
--- a/drivers/acpi/acpica/tbprint.c
+++ b/drivers/acpi/acpica/tbprint.c
@@ -127,18 +127,12 @@ acpi_tb_print_table_header(acpi_physical_address address,
{
struct acpi_table_header local_header;
- /*
- * The reason that we use ACPI_PRINTF_UINT and ACPI_FORMAT_TO_UINT is to
- * support both 32-bit and 64-bit hosts/addresses in a consistent manner.
- * The %p specifier does not emit uniform output on all hosts. On some,
- * leading zeros are not supported.
- */
if (ACPI_COMPARE_NAME(header->signature, ACPI_SIG_FACS)) {
/* FACS only has signature and length fields */
- ACPI_INFO((AE_INFO, "%-4.4s " ACPI_PRINTF_UINT " %06X",
- header->signature, ACPI_FORMAT_TO_UINT(address),
+ ACPI_INFO((AE_INFO, "%-4.4s 0x%8.8X%8.8X %06X",
+ header->signature, ACPI_FORMAT_UINT64(address),
header->length));
} else if (ACPI_VALIDATE_RSDP_SIG(header->signature)) {
@@ -149,9 +143,8 @@ acpi_tb_print_table_header(acpi_physical_address address,
header)->oem_id, ACPI_OEM_ID_SIZE);
acpi_tb_fix_string(local_header.oem_id, ACPI_OEM_ID_SIZE);
- ACPI_INFO((AE_INFO,
- "RSDP " ACPI_PRINTF_UINT " %06X (v%.2d %-6.6s)",
- ACPI_FORMAT_TO_UINT(address),
+ ACPI_INFO((AE_INFO, "RSDP 0x%8.8X%8.8X %06X (v%.2d %-6.6s)",
+ ACPI_FORMAT_UINT64(address),
(ACPI_CAST_PTR(struct acpi_table_rsdp, header)->
revision >
0) ? ACPI_CAST_PTR(struct acpi_table_rsdp,
@@ -165,9 +158,9 @@ acpi_tb_print_table_header(acpi_physical_address address,
acpi_tb_cleanup_table_header(&local_header, header);
ACPI_INFO((AE_INFO,
- "%-4.4s " ACPI_PRINTF_UINT
+ "%-4.4s 0x%8.8X%8.8X"
" %06X (v%.2d %-6.6s %-8.8s %08X %-4.4s %08X)",
- local_header.signature, ACPI_FORMAT_TO_UINT(address),
+ local_header.signature, ACPI_FORMAT_UINT64(address),
local_header.length, local_header.revision,
local_header.oem_id, local_header.oem_table_id,
local_header.oem_revision,
diff --git a/drivers/acpi/acpica/tbxfroot.c b/drivers/acpi/acpica/tbxfroot.c
index eac52cf14f1a..fa76a3603aa1 100644
--- a/drivers/acpi/acpica/tbxfroot.c
+++ b/drivers/acpi/acpica/tbxfroot.c
@@ -142,7 +142,7 @@ acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp * rsdp)
*
******************************************************************************/
-acpi_status __init acpi_find_root_pointer(acpi_size *table_address)
+acpi_status __init acpi_find_root_pointer(acpi_physical_address * table_address)
{
u8 *table_ptr;
u8 *mem_rover;
@@ -200,7 +200,8 @@ acpi_status __init acpi_find_root_pointer(acpi_size *table_address)
physical_address +=
(u32) ACPI_PTR_DIFF(mem_rover, table_ptr);
- *table_address = physical_address;
+ *table_address =
+ (acpi_physical_address) physical_address;
return_ACPI_STATUS(AE_OK);
}
}
@@ -233,7 +234,7 @@ acpi_status __init acpi_find_root_pointer(acpi_size *table_address)
(ACPI_HI_RSDP_WINDOW_BASE +
ACPI_PTR_DIFF(mem_rover, table_ptr));
- *table_address = physical_address;
+ *table_address = (acpi_physical_address) physical_address;
return_ACPI_STATUS(AE_OK);
}
diff --git a/drivers/acpi/acpica/utaddress.c b/drivers/acpi/acpica/utaddress.c
index 1279f50da757..911ea8e7fe87 100644
--- a/drivers/acpi/acpica/utaddress.c
+++ b/drivers/acpi/acpica/utaddress.c
@@ -107,10 +107,10 @@ acpi_ut_add_address_range(acpi_adr_space_type space_id,
acpi_gbl_address_range_list[space_id] = range_info;
ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
- "\nAdded [%4.4s] address range: 0x%p-0x%p\n",
+ "\nAdded [%4.4s] address range: 0x%8.8X%8.8X-0x%8.8X%8.8X\n",
acpi_ut_get_node_name(range_info->region_node),
- ACPI_CAST_PTR(void, address),
- ACPI_CAST_PTR(void, range_info->end_address)));
+ ACPI_FORMAT_UINT64(address),
+ ACPI_FORMAT_UINT64(range_info->end_address)));
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
return_ACPI_STATUS(AE_OK);
@@ -160,15 +160,13 @@ acpi_ut_remove_address_range(acpi_adr_space_type space_id,
}
ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
- "\nRemoved [%4.4s] address range: 0x%p-0x%p\n",
+ "\nRemoved [%4.4s] address range: 0x%8.8X%8.8X-0x%8.8X%8.8X\n",
acpi_ut_get_node_name(range_info->
region_node),
- ACPI_CAST_PTR(void,
- range_info->
- start_address),
- ACPI_CAST_PTR(void,
- range_info->
- end_address)));
+ ACPI_FORMAT_UINT64(range_info->
+ start_address),
+ ACPI_FORMAT_UINT64(range_info->
+ end_address)));
ACPI_FREE(range_info);
return_VOID;
@@ -245,16 +243,14 @@ acpi_ut_check_address_range(acpi_adr_space_type space_id,
region_node);
ACPI_WARNING((AE_INFO,
- "%s range 0x%p-0x%p conflicts with OpRegion 0x%p-0x%p (%s)",
+ "%s range 0x%8.8X%8.8X-0x%8.8X%8.8X conflicts with OpRegion 0x%8.8X%8.8X-0x%8.8X%8.8X (%s)",
acpi_ut_get_region_name(space_id),
- ACPI_CAST_PTR(void, address),
- ACPI_CAST_PTR(void, end_address),
- ACPI_CAST_PTR(void,
- range_info->
- start_address),
- ACPI_CAST_PTR(void,
- range_info->
- end_address),
+ ACPI_FORMAT_UINT64(address),
+ ACPI_FORMAT_UINT64(end_address),
+ ACPI_FORMAT_UINT64(range_info->
+ start_address),
+ ACPI_FORMAT_UINT64(range_info->
+ end_address),
pathname));
ACPI_FREE(pathname);
}
diff --git a/drivers/acpi/acpica/utbuffer.c b/drivers/acpi/acpica/utbuffer.c
index 242bd071f007..a8c39643e618 100644
--- a/drivers/acpi/acpica/utbuffer.c
+++ b/drivers/acpi/acpica/utbuffer.c
@@ -150,6 +150,14 @@ void acpi_ut_dump_buffer(u8 *buffer, u32 count, u32 display, u32 base_offset)
return;
}
+ /*
+ * Add comment characters so rest of line is ignored when
+ * compiled
+ */
+ if (j == 0) {
+ acpi_os_printf("// ");
+ }
+
buf_char = buffer[(acpi_size) i + j];
if (ACPI_IS_PRINT(buf_char)) {
acpi_os_printf("%c", buf_char);
diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c
index 5e8df9177da4..a72685c1e819 100644
--- a/drivers/acpi/acpica/utglobal.c
+++ b/drivers/acpi/acpica/utglobal.c
@@ -102,12 +102,19 @@ const struct acpi_predefined_names acpi_gbl_pre_defined_names[] = {
{"_SB_", ACPI_TYPE_DEVICE, NULL},
{"_SI_", ACPI_TYPE_LOCAL_SCOPE, NULL},
{"_TZ_", ACPI_TYPE_DEVICE, NULL},
- {"_REV", ACPI_TYPE_INTEGER, (char *)ACPI_CA_SUPPORT_LEVEL},
+ /*
+ * March, 2015:
+ * The _REV object is in the process of being deprecated, because
+ * other ACPI implementations permanently return 2. Thus, it
+ * has little or no value. Return 2 for compatibility with
+ * other ACPI implementations.
+ */
+ {"_REV", ACPI_TYPE_INTEGER, ACPI_CAST_PTR(char, 2)},
{"_OS_", ACPI_TYPE_STRING, ACPI_OS_NAME},
- {"_GL_", ACPI_TYPE_MUTEX, (char *)1},
+ {"_GL_", ACPI_TYPE_MUTEX, ACPI_CAST_PTR(char, 1)},
#if !defined (ACPI_NO_METHOD_EXECUTION) || defined (ACPI_CONSTANT_EVAL_ONLY)
- {"_OSI", ACPI_TYPE_METHOD, (char *)1},
+ {"_OSI", ACPI_TYPE_METHOD, ACPI_CAST_PTR(char, 1)},
#endif
/* Table terminator */
diff --git a/drivers/acpi/acpica/utmisc.c b/drivers/acpi/acpica/utmisc.c
index 56bbacd576f2..cbb7034d28d8 100644
--- a/drivers/acpi/acpica/utmisc.c
+++ b/drivers/acpi/acpica/utmisc.c
@@ -75,6 +75,7 @@ u8 acpi_ut_is_pci_root_bridge(char *id)
return (FALSE);
}
+#if (defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP)
/*******************************************************************************
*
* FUNCTION: acpi_ut_is_aml_table
@@ -102,6 +103,7 @@ u8 acpi_ut_is_aml_table(struct acpi_table_header *table)
return (FALSE);
}
+#endif
/*******************************************************************************
*
diff --git a/drivers/acpi/acpica/utosi.c b/drivers/acpi/acpica/utosi.c
index 574cd3118313..44035abdbf29 100644
--- a/drivers/acpi/acpica/utosi.c
+++ b/drivers/acpi/acpica/utosi.c
@@ -100,6 +100,7 @@ static struct acpi_interface_info acpi_default_supported_interfaces[] = {
{"Windows 2009", NULL, 0, ACPI_OSI_WIN_7}, /* Windows 7 and Server 2008 R2 - Added 09/2009 */
{"Windows 2012", NULL, 0, ACPI_OSI_WIN_8}, /* Windows 8 and Server 2012 - Added 08/2012 */
{"Windows 2013", NULL, 0, ACPI_OSI_WIN_8}, /* Windows 8.1 and Server 2012 R2 - Added 01/2014 */
+ {"Windows 2015", NULL, 0, ACPI_OSI_WIN_10}, /* Windows 10 - Added 03/2015 */
/* Feature Group Strings */
diff --git a/drivers/acpi/acpica/utprint.c b/drivers/acpi/acpica/utprint.c
index 82ca9142e10d..2be6bd4bdc09 100644
--- a/drivers/acpi/acpica/utprint.c
+++ b/drivers/acpi/acpica/utprint.c
@@ -357,11 +357,11 @@ int
acpi_ut_vsnprintf(char *string,
acpi_size size, const char *format, va_list args)
{
- u8 base = 10;
- u8 type = 0;
- s32 width = -1;
- s32 precision = -1;
- char qualifier = 0;
+ u8 base;
+ u8 type;
+ s32 width;
+ s32 precision;
+ char qualifier;
u64 number;
char *pos;
char *end;
@@ -380,6 +380,9 @@ acpi_ut_vsnprintf(char *string,
continue;
}
+ type = 0;
+ base = 10;
+
/* Process sign */
do {
diff --git a/drivers/acpi/acpica/utstate.c b/drivers/acpi/acpica/utstate.c
index 8274cc16edc3..f201171c5dda 100644
--- a/drivers/acpi/acpica/utstate.c
+++ b/drivers/acpi/acpica/utstate.c
@@ -49,39 +49,6 @@ ACPI_MODULE_NAME("utstate")
/*******************************************************************************
*
- * FUNCTION: acpi_ut_create_pkg_state_and_push
- *
- * PARAMETERS: object - Object to be added to the new state
- * action - Increment/Decrement
- * state_list - List the state will be added to
- *
- * RETURN: Status
- *
- * DESCRIPTION: Create a new state and push it
- *
- ******************************************************************************/
-acpi_status
-acpi_ut_create_pkg_state_and_push(void *internal_object,
- void *external_object,
- u16 index,
- union acpi_generic_state **state_list)
-{
- union acpi_generic_state *state;
-
- ACPI_FUNCTION_ENTRY();
-
- state =
- acpi_ut_create_pkg_state(internal_object, external_object, index);
- if (!state) {
- return (AE_NO_MEMORY);
- }
-
- acpi_ut_push_generic_state(state_list, state);
- return (AE_OK);
-}
-
-/*******************************************************************************
- *
* FUNCTION: acpi_ut_push_generic_state
*
* PARAMETERS: list_head - Head of the state stack
@@ -92,7 +59,6 @@ acpi_ut_create_pkg_state_and_push(void *internal_object,
* DESCRIPTION: Push a state object onto a state stack
*
******************************************************************************/
-
void
acpi_ut_push_generic_state(union acpi_generic_state **list_head,
union acpi_generic_state *state)
diff --git a/drivers/acpi/acpica/utuuid.c b/drivers/acpi/acpica/utuuid.c
index c6149a212149..e6cab669bd9c 100644
--- a/drivers/acpi/acpica/utuuid.c
+++ b/drivers/acpi/acpica/utuuid.c
@@ -47,6 +47,7 @@
#define _COMPONENT ACPI_COMPILER
ACPI_MODULE_NAME("utuuid")
+#if (defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP || defined ACPI_HELP_APP)
/*
* UUID support functions.
*
@@ -94,3 +95,4 @@ void acpi_ut_convert_string_to_uuid(char *in_string, u8 *uuid_buffer)
1]);
}
}
+#endif
diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c
index 04faf6df959f..24424f3fef96 100644
--- a/drivers/bcma/driver_mips.c
+++ b/drivers/bcma/driver_mips.c
@@ -21,7 +21,7 @@
#include <linux/serial_reg.h>
#include <linux/time.h>
#ifdef CONFIG_BCM47XX
-#include <bcm47xx_nvram.h>
+#include <linux/bcm47xx_nvram.h>
#endif
enum bcma_boot_dev {
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index b99729e36860..cbddbaddb347 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -20,6 +20,19 @@ config IMX_WEIM
The WEIM(Wireless External Interface Module) works like a bus.
You can attach many different devices on it, such as NOR, onenand.
+config MIPS_CDMM
+ bool "MIPS Common Device Memory Map (CDMM) Driver"
+ depends on CPU_MIPSR2
+ help
+ Driver needed for the MIPS Common Device Memory Map bus in MIPS
+ cores. This bus is for per-CPU tightly coupled devices such as the
+ Fast Debug Channel (FDC).
+
+ For this to work, either your bootloader needs to enable the CDMM
+ region at an unused physical address on the boot CPU, or else your
+ platform code needs to implement mips_cdmm_phys_base() (see
+ asm/cdmm.h).
+
config MVEBU_MBUS
bool
depends on PLAT_ORION
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index 2973c18cbcc2..807dd17ef2f8 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o
obj-$(CONFIG_IMX_WEIM) += imx-weim.o
+obj-$(CONFIG_MIPS_CDMM) += mips_cdmm.o
obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o
obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
diff --git a/drivers/bus/mips_cdmm.c b/drivers/bus/mips_cdmm.c
new file mode 100644
index 000000000000..5bd792c68f9b
--- /dev/null
+++ b/drivers/bus/mips_cdmm.c
@@ -0,0 +1,716 @@
+/*
+ * Bus driver for MIPS Common Device Memory Map (CDMM).
+ *
+ * Copyright (C) 2014-2015 Imagination Technologies Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/atomic.h>
+#include <linux/err.h>
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <asm/cdmm.h>
+#include <asm/hazards.h>
+#include <asm/mipsregs.h>
+
+/* Access control and status register fields */
+#define CDMM_ACSR_DEVTYPE_SHIFT 24
+#define CDMM_ACSR_DEVTYPE (255ul << CDMM_ACSR_DEVTYPE_SHIFT)
+#define CDMM_ACSR_DEVSIZE_SHIFT 16
+#define CDMM_ACSR_DEVSIZE (31ul << CDMM_ACSR_DEVSIZE_SHIFT)
+#define CDMM_ACSR_DEVREV_SHIFT 12
+#define CDMM_ACSR_DEVREV (15ul << CDMM_ACSR_DEVREV_SHIFT)
+#define CDMM_ACSR_UW (1ul << 3)
+#define CDMM_ACSR_UR (1ul << 2)
+#define CDMM_ACSR_SW (1ul << 1)
+#define CDMM_ACSR_SR (1ul << 0)
+
+/* Each block of device registers is 64 bytes */
+#define CDMM_DRB_SIZE 64
+
+#define to_mips_cdmm_driver(d) container_of(d, struct mips_cdmm_driver, drv)
+
+/* Default physical base address */
+static phys_addr_t mips_cdmm_default_base;
+
+/* Bus operations */
+
+static const struct mips_cdmm_device_id *
+mips_cdmm_lookup(const struct mips_cdmm_device_id *table,
+ struct mips_cdmm_device *dev)
+{
+ int ret = 0;
+
+ for (; table->type; ++table) {
+ ret = (dev->type == table->type);
+ if (ret)
+ break;
+ }
+
+ return ret ? table : NULL;
+}
+
+static int mips_cdmm_match(struct device *dev, struct device_driver *drv)
+{
+ struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
+ struct mips_cdmm_driver *cdrv = to_mips_cdmm_driver(drv);
+
+ return mips_cdmm_lookup(cdrv->id_table, cdev) != NULL;
+}
+
+static int mips_cdmm_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
+ int retval = 0;
+
+ retval = add_uevent_var(env, "CDMM_CPU=%u", cdev->cpu);
+ if (retval)
+ return retval;
+
+ retval = add_uevent_var(env, "CDMM_TYPE=0x%02x", cdev->type);
+ if (retval)
+ return retval;
+
+ retval = add_uevent_var(env, "CDMM_REV=%u", cdev->rev);
+ if (retval)
+ return retval;
+
+ retval = add_uevent_var(env, "MODALIAS=mipscdmm:t%02X", cdev->type);
+ return retval;
+}
+
+/* Device attributes */
+
+#define CDMM_ATTR(name, fmt, arg...) \
+static ssize_t name##_show(struct device *_dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct mips_cdmm_device *dev = to_mips_cdmm_device(_dev); \
+ return sprintf(buf, fmt, arg); \
+} \
+static DEVICE_ATTR_RO(name);
+
+CDMM_ATTR(cpu, "%u\n", dev->cpu);
+CDMM_ATTR(type, "0x%02x\n", dev->type);
+CDMM_ATTR(revision, "%u\n", dev->rev);
+CDMM_ATTR(modalias, "mipscdmm:t%02X\n", dev->type);
+CDMM_ATTR(resource, "\t%016llx\t%016llx\t%016lx\n",
+ (unsigned long long)dev->res.start,
+ (unsigned long long)dev->res.end,
+ dev->res.flags);
+
+static struct attribute *mips_cdmm_dev_attrs[] = {
+ &dev_attr_cpu.attr,
+ &dev_attr_type.attr,
+ &dev_attr_revision.attr,
+ &dev_attr_modalias.attr,
+ &dev_attr_resource.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(mips_cdmm_dev);
+
+struct bus_type mips_cdmm_bustype = {
+ .name = "cdmm",
+ .dev_groups = mips_cdmm_dev_groups,
+ .match = mips_cdmm_match,
+ .uevent = mips_cdmm_uevent,
+};
+EXPORT_SYMBOL_GPL(mips_cdmm_bustype);
+
+/*
+ * Standard driver callback helpers.
+ *
+ * All the CDMM driver callbacks need to be executed on the appropriate CPU from
+ * workqueues. For the standard driver callbacks we need a work function
+ * (mips_cdmm_{void,int}_work()) to do the actual call from the right CPU, and a
+ * wrapper function (generated with BUILD_PERCPU_HELPER) to arrange for the work
+ * function to be called on that CPU.
+ */
+
+/**
+ * struct mips_cdmm_work_dev - Data for per-device call work.
+ * @fn: CDMM driver callback function to call for the device.
+ * @dev: CDMM device to pass to @fn.
+ */
+struct mips_cdmm_work_dev {
+ void *fn;
+ struct mips_cdmm_device *dev;
+};
+
+/**
+ * mips_cdmm_void_work() - Call a void returning CDMM driver callback.
+ * @data: struct mips_cdmm_work_dev pointer.
+ *
+ * A work_on_cpu() callback function to call an arbitrary CDMM driver callback
+ * function which doesn't return a value.
+ */
+static long mips_cdmm_void_work(void *data)
+{
+ struct mips_cdmm_work_dev *work = data;
+ void (*fn)(struct mips_cdmm_device *) = work->fn;
+
+ fn(work->dev);
+ return 0;
+}
+
+/**
+ * mips_cdmm_int_work() - Call an int returning CDMM driver callback.
+ * @data: struct mips_cdmm_work_dev pointer.
+ *
+ * A work_on_cpu() callback function to call an arbitrary CDMM driver callback
+ * function which returns an int.
+ */
+static long mips_cdmm_int_work(void *data)
+{
+ struct mips_cdmm_work_dev *work = data;
+ int (*fn)(struct mips_cdmm_device *) = work->fn;
+
+ return fn(work->dev);
+}
+
+#define _BUILD_RET_void
+#define _BUILD_RET_int return
+
+/**
+ * BUILD_PERCPU_HELPER() - Helper to call a CDMM driver callback on right CPU.
+ * @_ret: Return type (void or int).
+ * @_name: Name of CDMM driver callback function.
+ *
+ * Generates a specific device callback function to call a CDMM driver callback
+ * function on the appropriate CPU for the device, and if applicable return the
+ * result.
+ */
+#define BUILD_PERCPU_HELPER(_ret, _name) \
+static _ret mips_cdmm_##_name(struct device *dev) \
+{ \
+ struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev); \
+ struct mips_cdmm_driver *cdrv = to_mips_cdmm_driver(dev->driver); \
+ struct mips_cdmm_work_dev work = { \
+ .fn = cdrv->_name, \
+ .dev = cdev, \
+ }; \
+ \
+ _BUILD_RET_##_ret work_on_cpu(cdev->cpu, \
+ mips_cdmm_##_ret##_work, &work); \
+}
+
+/* Driver callback functions */
+BUILD_PERCPU_HELPER(int, probe) /* int mips_cdmm_probe(struct device) */
+BUILD_PERCPU_HELPER(int, remove) /* int mips_cdmm_remove(struct device) */
+BUILD_PERCPU_HELPER(void, shutdown) /* void mips_cdmm_shutdown(struct device) */
+
+
+/* Driver registration */
+
+/**
+ * mips_cdmm_driver_register() - Register a CDMM driver.
+ * @drv: CDMM driver information.
+ *
+ * Register a CDMM driver with the CDMM subsystem. The driver will be informed
+ * of matching devices which are discovered.
+ *
+ * Returns: 0 on success.
+ */
+int mips_cdmm_driver_register(struct mips_cdmm_driver *drv)
+{
+ drv->drv.bus = &mips_cdmm_bustype;
+
+ if (drv->probe)
+ drv->drv.probe = mips_cdmm_probe;
+ if (drv->remove)
+ drv->drv.remove = mips_cdmm_remove;
+ if (drv->shutdown)
+ drv->drv.shutdown = mips_cdmm_shutdown;
+
+ return driver_register(&drv->drv);
+}
+EXPORT_SYMBOL_GPL(mips_cdmm_driver_register);
+
+/**
+ * mips_cdmm_driver_unregister() - Unregister a CDMM driver.
+ * @drv: CDMM driver information.
+ *
+ * Unregister a CDMM driver from the CDMM subsystem.
+ */
+void mips_cdmm_driver_unregister(struct mips_cdmm_driver *drv)
+{
+ driver_unregister(&drv->drv);
+}
+EXPORT_SYMBOL_GPL(mips_cdmm_driver_unregister);
+
+
+/* CDMM initialisation and bus discovery */
+
+/**
+ * struct mips_cdmm_bus - Info about CDMM bus.
+ * @phys: Physical address at which it is mapped.
+ * @regs: Virtual address where registers can be accessed.
+ * @drbs: Total number of DRBs.
+ * @drbs_reserved: Number of DRBs reserved.
+ * @discovered: Whether the devices on the bus have been discovered yet.
+ * @offline: Whether the CDMM bus is going offline (or very early
+ * coming back online), in which case it should be
+ * reconfigured each time.
+ */
+struct mips_cdmm_bus {
+ phys_addr_t phys;
+ void __iomem *regs;
+ unsigned int drbs;
+ unsigned int drbs_reserved;
+ bool discovered;
+ bool offline;
+};
+
+static struct mips_cdmm_bus mips_cdmm_boot_bus;
+static DEFINE_PER_CPU(struct mips_cdmm_bus *, mips_cdmm_buses);
+static atomic_t mips_cdmm_next_id = ATOMIC_INIT(-1);
+
+/**
+ * mips_cdmm_get_bus() - Get the per-CPU CDMM bus information.
+ *
+ * Get information about the per-CPU CDMM bus, if the bus is present.
+ *
+ * The caller must prevent migration to another CPU, either by disabling
+ * pre-emption or by running from a pinned kernel thread.
+ *
+ * Returns: Pointer to CDMM bus information for the current CPU.
+ * May return ERR_PTR(-errno) in case of error, so check with
+ * IS_ERR().
+ */
+static struct mips_cdmm_bus *mips_cdmm_get_bus(void)
+{
+ struct mips_cdmm_bus *bus, **bus_p;
+ unsigned long flags;
+ unsigned int cpu;
+
+ if (!cpu_has_cdmm)
+ return ERR_PTR(-ENODEV);
+
+ cpu = smp_processor_id();
+ /* Avoid early use of per-cpu primitives before initialised */
+ if (cpu == 0)
+ return &mips_cdmm_boot_bus;
+
+ /* Get bus pointer */
+ bus_p = per_cpu_ptr(&mips_cdmm_buses, cpu);
+ local_irq_save(flags);
+ bus = *bus_p;
+ /* Attempt allocation if NULL */
+ if (unlikely(!bus)) {
+ bus = kzalloc(sizeof(*bus), GFP_ATOMIC);
+ if (unlikely(!bus))
+ bus = ERR_PTR(-ENOMEM);
+ else
+ *bus_p = bus;
+ }
+ local_irq_restore(flags);
+ return bus;
+}
+
+/**
+ * mips_cdmm_cur_base() - Find current physical base address of CDMM region.
+ *
+ * Returns: Physical base address of CDMM region according to cdmmbase CP0
+ * register, or 0 if the CDMM region is disabled.
+ */
+static phys_addr_t mips_cdmm_cur_base(void)
+{
+ unsigned long cdmmbase = read_c0_cdmmbase();
+
+ if (!(cdmmbase & MIPS_CDMMBASE_EN))
+ return 0;
+
+ return (cdmmbase >> MIPS_CDMMBASE_ADDR_SHIFT)
+ << MIPS_CDMMBASE_ADDR_START;
+}
+
+/**
+ * mips_cdmm_setup() - Ensure the CDMM bus is initialised and usable.
+ * @bus: Pointer to bus information for current CPU.
+ * IS_ERR(bus) is checked, so no need for caller to check.
+ *
+ * The caller must prevent migration to another CPU, either by disabling
+ * pre-emption or by running from a pinned kernel thread.
+ *
+ * Returns 0 on success, -errno on failure.
+ */
+static int mips_cdmm_setup(struct mips_cdmm_bus *bus)
+{
+ unsigned long cdmmbase, flags;
+ int ret = 0;
+
+ if (IS_ERR(bus))
+ return PTR_ERR(bus);
+
+ local_irq_save(flags);
+ /* Don't set up bus a second time unless marked offline */
+ if (bus->offline) {
+ /* If CDMM region is still set up, nothing to do */
+ if (bus->phys == mips_cdmm_cur_base())
+ goto out;
+ /*
+ * The CDMM region isn't set up as expected, so it needs
+ * reconfiguring, but then we can stop checking it.
+ */
+ bus->offline = false;
+ } else if (bus->phys > 1) {
+ goto out;
+ }
+
+ /* If the CDMM region is already configured, inherit that setup */
+ if (!bus->phys)
+ bus->phys = mips_cdmm_cur_base();
+ /* Otherwise, ask platform code for suggestions */
+ if (!bus->phys && mips_cdmm_phys_base)
+ bus->phys = mips_cdmm_phys_base();
+ /* Otherwise, copy what other CPUs have done */
+ if (!bus->phys)
+ bus->phys = mips_cdmm_default_base;
+ /* Otherwise, complain once */
+ if (!bus->phys) {
+ bus->phys = 1;
+ /*
+ * If you hit this, either your bootloader needs to set up the
+ * CDMM on the boot CPU, or else you need to implement
+ * mips_cdmm_phys_base() for your platform (see asm/cdmm.h).
+ */
+ pr_err("cdmm%u: Failed to choose a physical base\n",
+ smp_processor_id());
+ }
+ /* Already complained? */
+ if (bus->phys == 1) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ /* Record our success for other CPUs to copy */
+ mips_cdmm_default_base = bus->phys;
+
+ pr_debug("cdmm%u: Enabling CDMM region at %pa\n",
+ smp_processor_id(), &bus->phys);
+
+ /* Enable CDMM */
+ cdmmbase = read_c0_cdmmbase();
+ cdmmbase &= (1ul << MIPS_CDMMBASE_ADDR_SHIFT) - 1;
+ cdmmbase |= (bus->phys >> MIPS_CDMMBASE_ADDR_START)
+ << MIPS_CDMMBASE_ADDR_SHIFT;
+ cdmmbase |= MIPS_CDMMBASE_EN;
+ write_c0_cdmmbase(cdmmbase);
+ tlbw_use_hazard();
+
+ bus->regs = (void __iomem *)CKSEG1ADDR(bus->phys);
+ bus->drbs = 1 + ((cdmmbase & MIPS_CDMMBASE_SIZE) >>
+ MIPS_CDMMBASE_SIZE_SHIFT);
+ bus->drbs_reserved = !!(cdmmbase & MIPS_CDMMBASE_CI);
+
+out:
+ local_irq_restore(flags);
+ return ret;
+}
+
+/**
+ * mips_cdmm_early_probe() - Minimally probe for a specific device on CDMM.
+ * @dev_type: CDMM type code to look for.
+ *
+ * Minimally configure the in-CPU Common Device Memory Map (CDMM) and look for a
+ * specific device. This can be used to find a device very early in boot for
+ * example to configure an early FDC console device.
+ *
+ * The caller must prevent migration to another CPU, either by disabling
+ * pre-emption or by running from a pinned kernel thread.
+ *
+ * Returns: MMIO pointer to device memory. The caller can read the ACSR
+ * register to find more information about the device (such as the
+ * version number or the number of blocks).
+ * May return IOMEM_ERR_PTR(-errno) in case of error, so check with
+ * IS_ERR().
+ */
+void __iomem *mips_cdmm_early_probe(unsigned int dev_type)
+{
+ struct mips_cdmm_bus *bus;
+ void __iomem *cdmm;
+ u32 acsr;
+ unsigned int drb, type, size;
+ int err;
+
+ if (WARN_ON(!dev_type))
+ return IOMEM_ERR_PTR(-ENODEV);
+
+ bus = mips_cdmm_get_bus();
+ err = mips_cdmm_setup(bus);
+ if (err)
+ return IOMEM_ERR_PTR(err);
+
+ /* Skip the first block if it's reserved for more registers */
+ drb = bus->drbs_reserved;
+ cdmm = bus->regs;
+
+ /* Look for a specific device type */
+ for (; drb < bus->drbs; drb += size + 1) {
+ acsr = readl(cdmm + drb * CDMM_DRB_SIZE);
+ type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT;
+ if (type == dev_type)
+ return cdmm + drb * CDMM_DRB_SIZE;
+ size = (acsr & CDMM_ACSR_DEVSIZE) >> CDMM_ACSR_DEVSIZE_SHIFT;
+ }
+
+ return IOMEM_ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL_GPL(mips_cdmm_early_probe);
+
+/**
+ * mips_cdmm_release() - Release a removed CDMM device.
+ * @dev: Device object
+ *
+ * Clean up the struct mips_cdmm_device for an unused CDMM device. This is
+ * called automatically by the driver core when a device is removed.
+ */
+static void mips_cdmm_release(struct device *dev)
+{
+ struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
+
+ kfree(cdev);
+}
+
+/**
+ * mips_cdmm_bus_discover() - Discover the devices on the CDMM bus.
+ * @bus: CDMM bus information, must already be set up.
+ */
+static void mips_cdmm_bus_discover(struct mips_cdmm_bus *bus)
+{
+ void __iomem *cdmm;
+ u32 acsr;
+ unsigned int drb, type, size, rev;
+ struct mips_cdmm_device *dev;
+ unsigned int cpu = smp_processor_id();
+ int ret = 0;
+ int id = 0;
+
+ /* Skip the first block if it's reserved for more registers */
+ drb = bus->drbs_reserved;
+ cdmm = bus->regs;
+
+ /* Discover devices */
+ bus->discovered = true;
+ pr_info("cdmm%u discovery (%u blocks)\n", cpu, bus->drbs);
+ for (; drb < bus->drbs; drb += size + 1) {
+ acsr = readl(cdmm + drb * CDMM_DRB_SIZE);
+ type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT;
+ size = (acsr & CDMM_ACSR_DEVSIZE) >> CDMM_ACSR_DEVSIZE_SHIFT;
+ rev = (acsr & CDMM_ACSR_DEVREV) >> CDMM_ACSR_DEVREV_SHIFT;
+
+ if (!type)
+ continue;
+
+ pr_info("cdmm%u-%u: @%u (%#x..%#x), type 0x%02x, rev %u\n",
+ cpu, id, drb, drb * CDMM_DRB_SIZE,
+ (drb + size + 1) * CDMM_DRB_SIZE - 1,
+ type, rev);
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ break;
+
+ dev->cpu = cpu;
+ dev->res.start = bus->phys + drb * CDMM_DRB_SIZE;
+ dev->res.end = bus->phys +
+ (drb + size + 1) * CDMM_DRB_SIZE - 1;
+ dev->res.flags = IORESOURCE_MEM;
+ dev->type = type;
+ dev->rev = rev;
+ dev->dev.parent = get_cpu_device(cpu);
+ dev->dev.bus = &mips_cdmm_bustype;
+ dev->dev.id = atomic_inc_return(&mips_cdmm_next_id);
+ dev->dev.release = mips_cdmm_release;
+
+ dev_set_name(&dev->dev, "cdmm%u-%u", cpu, id);
+ ++id;
+ ret = device_register(&dev->dev);
+ if (ret) {
+ put_device(&dev->dev);
+ kfree(dev);
+ }
+ }
+}
+
+
+/*
+ * CPU hotplug and initialisation
+ *
+ * All the CDMM driver callbacks need to be executed on the appropriate CPU from
+ * workqueues. For the CPU callbacks, they need to be called for all devices on
+ * that CPU, so the work function calls bus_for_each_dev, using a helper
+ * (generated with BUILD_PERDEV_HELPER) to call the driver callback if the
+ * device's CPU matches.
+ */
+
+/**
+ * BUILD_PERDEV_HELPER() - Helper to call a CDMM driver callback if CPU matches.
+ * @_name: Name of CDMM driver callback function.
+ *
+ * Generates a bus_for_each_dev callback function to call a specific CDMM driver
+ * callback function for the device if the device's CPU matches that pointed to
+ * by the data argument.
+ *
+ * This is used for informing drivers for all devices on a given CPU of some
+ * event (such as the CPU going online/offline).
+ *
+ * It is expected to already be called from the appropriate CPU.
+ */
+#define BUILD_PERDEV_HELPER(_name) \
+static int mips_cdmm_##_name##_helper(struct device *dev, void *data) \
+{ \
+ struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev); \
+ struct mips_cdmm_driver *cdrv; \
+ unsigned int cpu = *(unsigned int *)data; \
+ \
+ if (cdev->cpu != cpu || !dev->driver) \
+ return 0; \
+ \
+ cdrv = to_mips_cdmm_driver(dev->driver); \
+ if (!cdrv->_name) \
+ return 0; \
+ return cdrv->_name(cdev); \
+}
+
+/* bus_for_each_dev callback helper functions */
+BUILD_PERDEV_HELPER(cpu_down) /* int mips_cdmm_cpu_down_helper(...) */
+BUILD_PERDEV_HELPER(cpu_up) /* int mips_cdmm_cpu_up_helper(...) */
+
+/**
+ * mips_cdmm_bus_down() - Tear down the CDMM bus.
+ * @data: Pointer to unsigned int CPU number.
+ *
+ * This work_on_cpu callback function is executed on a given CPU to call the
+ * CDMM driver cpu_down callback for all devices on that CPU.
+ */
+static long mips_cdmm_bus_down(void *data)
+{
+ struct mips_cdmm_bus *bus;
+ long ret;
+
+ /* Inform all the devices on the bus */
+ ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, data,
+ mips_cdmm_cpu_down_helper);
+
+ /*
+ * While bus is offline, each use of it should reconfigure it just in
+ * case it is first use when coming back online again.
+ */
+ bus = mips_cdmm_get_bus();
+ if (!IS_ERR(bus))
+ bus->offline = true;
+
+ return ret;
+}
+
+/**
+ * mips_cdmm_bus_up() - Bring up the CDMM bus.
+ * @data: Pointer to unsigned int CPU number.
+ *
+ * This work_on_cpu callback function is executed on a given CPU to discover
+ * CDMM devices on that CPU, or to call the CDMM driver cpu_up callback for all
+ * devices already discovered on that CPU.
+ *
+ * It is used during initialisation and when CPUs are brought online.
+ */
+static long mips_cdmm_bus_up(void *data)
+{
+ struct mips_cdmm_bus *bus;
+ long ret;
+
+ bus = mips_cdmm_get_bus();
+ ret = mips_cdmm_setup(bus);
+ if (ret)
+ return ret;
+
+ /* Bus now set up, so we can drop the offline flag if still set */
+ bus->offline = false;
+
+ if (!bus->discovered)
+ mips_cdmm_bus_discover(bus);
+ else
+ /* Inform all the devices on the bus */
+ ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, data,
+ mips_cdmm_cpu_up_helper);
+
+ return ret;
+}
+
+/**
+ * mips_cdmm_cpu_notify() - Take action when a CPU is going online or offline.
+ * @nb: CPU notifier block .
+ * @action: Event that has taken place (CPU_*).
+ * @data: CPU number.
+ *
+ * This notifier is used to keep the CDMM buses updated as CPUs are offlined and
+ * onlined. When CPUs go offline or come back online, so does their CDMM bus, so
+ * devices must be informed. Also when CPUs come online for the first time the
+ * devices on the CDMM bus need discovering.
+ *
+ * Returns: NOTIFY_OK if event was used.
+ * NOTIFY_DONE if we didn't care.
+ */
+static int mips_cdmm_cpu_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ unsigned int cpu = (unsigned int)data;
+
+ switch (action & ~CPU_TASKS_FROZEN) {
+ case CPU_ONLINE:
+ case CPU_DOWN_FAILED:
+ work_on_cpu(cpu, mips_cdmm_bus_up, &cpu);
+ break;
+ case CPU_DOWN_PREPARE:
+ work_on_cpu(cpu, mips_cdmm_bus_down, &cpu);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block mips_cdmm_cpu_nb = {
+ .notifier_call = mips_cdmm_cpu_notify,
+};
+
+/**
+ * mips_cdmm_init() - Initialise CDMM bus.
+ *
+ * Initialise CDMM bus, discover CDMM devices for online CPUs, and arrange for
+ * hotplug notifications so the CDMM drivers can be kept up to date.
+ */
+static int __init mips_cdmm_init(void)
+{
+ unsigned int cpu;
+ int ret;
+
+ /* Register the bus */
+ ret = bus_register(&mips_cdmm_bustype);
+ if (ret)
+ return ret;
+
+ /* We want to be notified about new CPUs */
+ ret = register_cpu_notifier(&mips_cdmm_cpu_nb);
+ if (ret) {
+ pr_warn("cdmm: Failed to register CPU notifier\n");
+ goto out;
+ }
+
+ /* Discover devices on CDMM of online CPUs */
+ for_each_online_cpu(cpu)
+ work_on_cpu(cpu, mips_cdmm_bus_up, &cpu);
+
+ return 0;
+out:
+ bus_unregister(&mips_cdmm_bustype);
+ return ret;
+}
+subsys_initcall(mips_cdmm_init);
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index d478ceb69c5f..e43ff53f85a6 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_ARCH_MMP) += mmp/
endif
obj-$(CONFIG_PLAT_ORION) += mvebu/
obj-$(CONFIG_ARCH_MXS) += mxs/
+obj-$(CONFIG_MACH_PISTACHIO) += pistachio/
obj-$(CONFIG_COMMON_CLK_PXA) += pxa/
obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
diff --git a/drivers/clk/pistachio/Makefile b/drivers/clk/pistachio/Makefile
new file mode 100644
index 000000000000..f1e151fbef65
--- /dev/null
+++ b/drivers/clk/pistachio/Makefile
@@ -0,0 +1,3 @@
+obj-y += clk.o
+obj-y += clk-pll.o
+obj-y += clk-pistachio.o
diff --git a/drivers/clk/pistachio/clk-pistachio.c b/drivers/clk/pistachio/clk-pistachio.c
new file mode 100644
index 000000000000..8c0fe8828f99
--- /dev/null
+++ b/drivers/clk/pistachio/clk-pistachio.c
@@ -0,0 +1,329 @@
+/*
+ * Pistachio SoC clock controllers
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+
+#include <dt-bindings/clock/pistachio-clk.h>
+
+#include "clk.h"
+
+static struct pistachio_gate pistachio_gates[] __initdata = {
+ GATE(CLK_MIPS, "mips", "mips_div", 0x104, 0),
+ GATE(CLK_AUDIO_IN, "audio_in", "audio_clk_in_gate", 0x104, 1),
+ GATE(CLK_AUDIO, "audio", "audio_div", 0x104, 2),
+ GATE(CLK_I2S, "i2s", "i2s_div", 0x104, 3),
+ GATE(CLK_SPDIF, "spdif", "spdif_div", 0x104, 4),
+ GATE(CLK_AUDIO_DAC, "audio_dac", "audio_dac_div", 0x104, 5),
+ GATE(CLK_RPU_V, "rpu_v", "rpu_v_div", 0x104, 6),
+ GATE(CLK_RPU_L, "rpu_l", "rpu_l_div", 0x104, 7),
+ GATE(CLK_RPU_SLEEP, "rpu_sleep", "rpu_sleep_div", 0x104, 8),
+ GATE(CLK_WIFI_PLL_GATE, "wifi_pll_gate", "wifi_pll_mux", 0x104, 9),
+ GATE(CLK_RPU_CORE, "rpu_core", "rpu_core_div", 0x104, 10),
+ GATE(CLK_WIFI_ADC, "wifi_adc", "wifi_div8_mux", 0x104, 11),
+ GATE(CLK_WIFI_DAC, "wifi_dac", "wifi_div4_mux", 0x104, 12),
+ GATE(CLK_USB_PHY, "usb_phy", "usb_phy_div", 0x104, 13),
+ GATE(CLK_ENET_IN, "enet_in", "enet_clk_in_gate", 0x104, 14),
+ GATE(CLK_ENET, "enet", "enet_div", 0x104, 15),
+ GATE(CLK_UART0, "uart0", "uart0_div", 0x104, 16),
+ GATE(CLK_UART1, "uart1", "uart1_div", 0x104, 17),
+ GATE(CLK_PERIPH_SYS, "periph_sys", "sys_internal_div", 0x104, 18),
+ GATE(CLK_SPI0, "spi0", "spi0_div", 0x104, 19),
+ GATE(CLK_SPI1, "spi1", "spi1_div", 0x104, 20),
+ GATE(CLK_EVENT_TIMER, "event_timer", "event_timer_div", 0x104, 21),
+ GATE(CLK_AUX_ADC_INTERNAL, "aux_adc_internal", "sys_internal_div",
+ 0x104, 22),
+ GATE(CLK_AUX_ADC, "aux_adc", "aux_adc_div", 0x104, 23),
+ GATE(CLK_SD_HOST, "sd_host", "sd_host_div", 0x104, 24),
+ GATE(CLK_BT, "bt", "bt_div", 0x104, 25),
+ GATE(CLK_BT_DIV4, "bt_div4", "bt_div4_div", 0x104, 26),
+ GATE(CLK_BT_DIV8, "bt_div8", "bt_div8_div", 0x104, 27),
+ GATE(CLK_BT_1MHZ, "bt_1mhz", "bt_1mhz_div", 0x104, 28),
+};
+
+static struct pistachio_fixed_factor pistachio_ffs[] __initdata = {
+ FIXED_FACTOR(CLK_WIFI_DIV4, "wifi_div4", "wifi_pll", 4),
+ FIXED_FACTOR(CLK_WIFI_DIV8, "wifi_div8", "wifi_pll", 8),
+};
+
+static struct pistachio_div pistachio_divs[] __initdata = {
+ DIV(CLK_MIPS_INTERNAL_DIV, "mips_internal_div", "mips_pll_mux",
+ 0x204, 2),
+ DIV(CLK_MIPS_DIV, "mips_div", "mips_internal_div", 0x208, 8),
+ DIV_F(CLK_AUDIO_DIV, "audio_div", "audio_mux",
+ 0x20c, 8, CLK_DIVIDER_ROUND_CLOSEST),
+ DIV_F(CLK_I2S_DIV, "i2s_div", "audio_pll_mux",
+ 0x210, 8, CLK_DIVIDER_ROUND_CLOSEST),
+ DIV_F(CLK_SPDIF_DIV, "spdif_div", "audio_pll_mux",
+ 0x214, 8, CLK_DIVIDER_ROUND_CLOSEST),
+ DIV_F(CLK_AUDIO_DAC_DIV, "audio_dac_div", "audio_pll_mux",
+ 0x218, 8, CLK_DIVIDER_ROUND_CLOSEST),
+ DIV(CLK_RPU_V_DIV, "rpu_v_div", "rpu_v_pll_mux", 0x21c, 2),
+ DIV(CLK_RPU_L_DIV, "rpu_l_div", "rpu_l_mux", 0x220, 2),
+ DIV(CLK_RPU_SLEEP_DIV, "rpu_sleep_div", "xtal", 0x224, 10),
+ DIV(CLK_RPU_CORE_DIV, "rpu_core_div", "rpu_core_mux", 0x228, 3),
+ DIV(CLK_USB_PHY_DIV, "usb_phy_div", "sys_internal_div", 0x22c, 6),
+ DIV(CLK_ENET_DIV, "enet_div", "enet_mux", 0x230, 6),
+ DIV_F(CLK_UART0_INTERNAL_DIV, "uart0_internal_div", "sys_pll_mux",
+ 0x234, 3, CLK_DIVIDER_ROUND_CLOSEST),
+ DIV_F(CLK_UART0_DIV, "uart0_div", "uart0_internal_div", 0x238, 10,
+ CLK_DIVIDER_ROUND_CLOSEST),
+ DIV_F(CLK_UART1_INTERNAL_DIV, "uart1_internal_div", "sys_pll_mux",
+ 0x23c, 3, CLK_DIVIDER_ROUND_CLOSEST),
+ DIV_F(CLK_UART1_DIV, "uart1_div", "uart1_internal_div", 0x240, 10,
+ CLK_DIVIDER_ROUND_CLOSEST),
+ DIV(CLK_SYS_INTERNAL_DIV, "sys_internal_div", "sys_pll_mux", 0x244, 3),
+ DIV(CLK_SPI0_INTERNAL_DIV, "spi0_internal_div", "sys_pll_mux",
+ 0x248, 3),
+ DIV(CLK_SPI0_DIV, "spi0_div", "spi0_internal_div", 0x24c, 7),
+ DIV(CLK_SPI1_INTERNAL_DIV, "spi1_internal_div", "sys_pll_mux",
+ 0x250, 3),
+ DIV(CLK_SPI1_DIV, "spi1_div", "spi1_internal_div", 0x254, 7),
+ DIV(CLK_EVENT_TIMER_INTERNAL_DIV, "event_timer_internal_div",
+ "event_timer_mux", 0x258, 3),
+ DIV(CLK_EVENT_TIMER_DIV, "event_timer_div", "event_timer_internal_div",
+ 0x25c, 12),
+ DIV(CLK_AUX_ADC_INTERNAL_DIV, "aux_adc_internal_div",
+ "aux_adc_internal", 0x260, 3),
+ DIV(CLK_AUX_ADC_DIV, "aux_adc_div", "aux_adc_internal_div", 0x264, 10),
+ DIV(CLK_SD_HOST_DIV, "sd_host_div", "sd_host_mux", 0x268, 6),
+ DIV(CLK_BT_DIV, "bt_div", "bt_pll_mux", 0x26c, 6),
+ DIV(CLK_BT_DIV4_DIV, "bt_div4_div", "bt_pll_mux", 0x270, 6),
+ DIV(CLK_BT_DIV8_DIV, "bt_div8_div", "bt_pll_mux", 0x274, 6),
+ DIV(CLK_BT_1MHZ_INTERNAL_DIV, "bt_1mhz_internal_div", "bt_pll_mux",
+ 0x278, 3),
+ DIV(CLK_BT_1MHZ_DIV, "bt_1mhz_div", "bt_1mhz_internal_div", 0x27c, 10),
+};
+
+PNAME(mux_xtal_audio_refclk) = { "xtal", "audio_clk_in_gate" };
+PNAME(mux_xtal_mips) = { "xtal", "mips_pll" };
+PNAME(mux_xtal_audio) = { "xtal", "audio_pll", "audio_in" };
+PNAME(mux_audio_debug) = { "audio_pll_mux", "debug_mux" };
+PNAME(mux_xtal_rpu_v) = { "xtal", "rpu_v_pll" };
+PNAME(mux_xtal_rpu_l) = { "xtal", "rpu_l_pll" };
+PNAME(mux_rpu_l_mips) = { "rpu_l_pll_mux", "mips_pll_mux" };
+PNAME(mux_xtal_wifi) = { "xtal", "wifi_pll" };
+PNAME(mux_xtal_wifi_div4) = { "xtal", "wifi_div4" };
+PNAME(mux_xtal_wifi_div8) = { "xtal", "wifi_div8" };
+PNAME(mux_wifi_div4_rpu_l) = { "wifi_pll_gate", "wifi_div4_mux",
+ "rpu_l_pll_mux" };
+PNAME(mux_xtal_sys) = { "xtal", "sys_pll" };
+PNAME(mux_sys_enet) = { "sys_internal_div", "enet_in" };
+PNAME(mux_audio_sys) = { "audio_pll_mux", "sys_internal_div" };
+PNAME(mux_sys_bt) = { "sys_internal_div", "bt_pll_mux" };
+PNAME(mux_xtal_bt) = { "xtal", "bt_pll" };
+
+static struct pistachio_mux pistachio_muxes[] __initdata = {
+ MUX(CLK_AUDIO_REF_MUX, "audio_refclk_mux", mux_xtal_audio_refclk,
+ 0x200, 0),
+ MUX(CLK_MIPS_PLL_MUX, "mips_pll_mux", mux_xtal_mips, 0x200, 1),
+ MUX(CLK_AUDIO_PLL_MUX, "audio_pll_mux", mux_xtal_audio, 0x200, 2),
+ MUX(CLK_AUDIO_MUX, "audio_mux", mux_audio_debug, 0x200, 4),
+ MUX(CLK_RPU_V_PLL_MUX, "rpu_v_pll_mux", mux_xtal_rpu_v, 0x200, 5),
+ MUX(CLK_RPU_L_PLL_MUX, "rpu_l_pll_mux", mux_xtal_rpu_l, 0x200, 6),
+ MUX(CLK_RPU_L_MUX, "rpu_l_mux", mux_rpu_l_mips, 0x200, 7),
+ MUX(CLK_WIFI_PLL_MUX, "wifi_pll_mux", mux_xtal_wifi, 0x200, 8),
+ MUX(CLK_WIFI_DIV4_MUX, "wifi_div4_mux", mux_xtal_wifi_div4, 0x200, 9),
+ MUX(CLK_WIFI_DIV8_MUX, "wifi_div8_mux", mux_xtal_wifi_div8, 0x200, 10),
+ MUX(CLK_RPU_CORE_MUX, "rpu_core_mux", mux_wifi_div4_rpu_l, 0x200, 11),
+ MUX(CLK_SYS_PLL_MUX, "sys_pll_mux", mux_xtal_sys, 0x200, 13),
+ MUX(CLK_ENET_MUX, "enet_mux", mux_sys_enet, 0x200, 14),
+ MUX(CLK_EVENT_TIMER_MUX, "event_timer_mux", mux_audio_sys, 0x200, 15),
+ MUX(CLK_SD_HOST_MUX, "sd_host_mux", mux_sys_bt, 0x200, 16),
+ MUX(CLK_BT_PLL_MUX, "bt_pll_mux", mux_xtal_bt, 0x200, 17),
+};
+
+static struct pistachio_pll pistachio_plls[] __initdata = {
+ PLL_FIXED(CLK_MIPS_PLL, "mips_pll", "xtal", PLL_GF40LP_LAINT, 0x0),
+ PLL_FIXED(CLK_AUDIO_PLL, "audio_pll", "audio_refclk_mux",
+ PLL_GF40LP_FRAC, 0xc),
+ PLL_FIXED(CLK_RPU_V_PLL, "rpu_v_pll", "xtal", PLL_GF40LP_LAINT, 0x20),
+ PLL_FIXED(CLK_RPU_L_PLL, "rpu_l_pll", "xtal", PLL_GF40LP_LAINT, 0x2c),
+ PLL_FIXED(CLK_SYS_PLL, "sys_pll", "xtal", PLL_GF40LP_FRAC, 0x38),
+ PLL_FIXED(CLK_WIFI_PLL, "wifi_pll", "xtal", PLL_GF40LP_FRAC, 0x4c),
+ PLL_FIXED(CLK_BT_PLL, "bt_pll", "xtal", PLL_GF40LP_LAINT, 0x60),
+};
+
+PNAME(mux_debug) = { "mips_pll_mux", "rpu_v_pll_mux",
+ "rpu_l_pll_mux", "sys_pll_mux",
+ "wifi_pll_mux", "bt_pll_mux" };
+static u32 mux_debug_idx[] = { 0x0, 0x1, 0x2, 0x4, 0x8, 0x10 };
+
+static unsigned int pistachio_critical_clks[] __initdata = {
+ CLK_MIPS,
+ CLK_PERIPH_SYS,
+};
+
+static void __init pistachio_clk_init(struct device_node *np)
+{
+ struct pistachio_clk_provider *p;
+ struct clk *debug_clk;
+
+ p = pistachio_clk_alloc_provider(np, CLK_NR_CLKS);
+ if (!p)
+ return;
+
+ pistachio_clk_register_pll(p, pistachio_plls,
+ ARRAY_SIZE(pistachio_plls));
+ pistachio_clk_register_mux(p, pistachio_muxes,
+ ARRAY_SIZE(pistachio_muxes));
+ pistachio_clk_register_div(p, pistachio_divs,
+ ARRAY_SIZE(pistachio_divs));
+ pistachio_clk_register_fixed_factor(p, pistachio_ffs,
+ ARRAY_SIZE(pistachio_ffs));
+ pistachio_clk_register_gate(p, pistachio_gates,
+ ARRAY_SIZE(pistachio_gates));
+
+ debug_clk = clk_register_mux_table(NULL, "debug_mux", mux_debug,
+ ARRAY_SIZE(mux_debug),
+ CLK_SET_RATE_NO_REPARENT,
+ p->base + 0x200, 18, 0x1f, 0,
+ mux_debug_idx, NULL);
+ p->clk_data.clks[CLK_DEBUG_MUX] = debug_clk;
+
+ pistachio_clk_register_provider(p);
+
+ pistachio_clk_force_enable(p, pistachio_critical_clks,
+ ARRAY_SIZE(pistachio_critical_clks));
+}
+CLK_OF_DECLARE(pistachio_clk, "img,pistachio-clk", pistachio_clk_init);
+
+static struct pistachio_gate pistachio_periph_gates[] __initdata = {
+ GATE(PERIPH_CLK_SYS, "sys", "periph_sys", 0x100, 0),
+ GATE(PERIPH_CLK_SYS_BUS, "bus_sys", "periph_sys", 0x100, 1),
+ GATE(PERIPH_CLK_DDR, "ddr", "periph_sys", 0x100, 2),
+ GATE(PERIPH_CLK_ROM, "rom", "rom_div", 0x100, 3),
+ GATE(PERIPH_CLK_COUNTER_FAST, "counter_fast", "counter_fast_div",
+ 0x100, 4),
+ GATE(PERIPH_CLK_COUNTER_SLOW, "counter_slow", "counter_slow_div",
+ 0x100, 5),
+ GATE(PERIPH_CLK_IR, "ir", "ir_div", 0x100, 6),
+ GATE(PERIPH_CLK_WD, "wd", "wd_div", 0x100, 7),
+ GATE(PERIPH_CLK_PDM, "pdm", "pdm_div", 0x100, 8),
+ GATE(PERIPH_CLK_PWM, "pwm", "pwm_div", 0x100, 9),
+ GATE(PERIPH_CLK_I2C0, "i2c0", "i2c0_div", 0x100, 10),
+ GATE(PERIPH_CLK_I2C1, "i2c1", "i2c1_div", 0x100, 11),
+ GATE(PERIPH_CLK_I2C2, "i2c2", "i2c2_div", 0x100, 12),
+ GATE(PERIPH_CLK_I2C3, "i2c3", "i2c3_div", 0x100, 13),
+};
+
+static struct pistachio_div pistachio_periph_divs[] __initdata = {
+ DIV(PERIPH_CLK_ROM_DIV, "rom_div", "periph_sys", 0x10c, 7),
+ DIV(PERIPH_CLK_COUNTER_FAST_DIV, "counter_fast_div", "periph_sys",
+ 0x110, 7),
+ DIV(PERIPH_CLK_COUNTER_SLOW_PRE_DIV, "counter_slow_pre_div",
+ "periph_sys", 0x114, 7),
+ DIV(PERIPH_CLK_COUNTER_SLOW_DIV, "counter_slow_div",
+ "counter_slow_pre_div", 0x118, 7),
+ DIV_F(PERIPH_CLK_IR_PRE_DIV, "ir_pre_div", "periph_sys", 0x11c, 7,
+ CLK_DIVIDER_ROUND_CLOSEST),
+ DIV_F(PERIPH_CLK_IR_DIV, "ir_div", "ir_pre_div", 0x120, 7,
+ CLK_DIVIDER_ROUND_CLOSEST),
+ DIV_F(PERIPH_CLK_WD_PRE_DIV, "wd_pre_div", "periph_sys", 0x124, 7,
+ CLK_DIVIDER_ROUND_CLOSEST),
+ DIV_F(PERIPH_CLK_WD_DIV, "wd_div", "wd_pre_div", 0x128, 7,
+ CLK_DIVIDER_ROUND_CLOSEST),
+ DIV(PERIPH_CLK_PDM_PRE_DIV, "pdm_pre_div", "periph_sys", 0x12c, 7),
+ DIV(PERIPH_CLK_PDM_DIV, "pdm_div", "pdm_pre_div", 0x130, 7),
+ DIV(PERIPH_CLK_PWM_PRE_DIV, "pwm_pre_div", "periph_sys", 0x134, 7),
+ DIV(PERIPH_CLK_PWM_DIV, "pwm_div", "pwm_pre_div", 0x138, 7),
+ DIV(PERIPH_CLK_I2C0_PRE_DIV, "i2c0_pre_div", "periph_sys", 0x13c, 7),
+ DIV(PERIPH_CLK_I2C0_DIV, "i2c0_div", "i2c0_pre_div", 0x140, 7),
+ DIV(PERIPH_CLK_I2C1_PRE_DIV, "i2c1_pre_div", "periph_sys", 0x144, 7),
+ DIV(PERIPH_CLK_I2C1_DIV, "i2c1_div", "i2c1_pre_div", 0x148, 7),
+ DIV(PERIPH_CLK_I2C2_PRE_DIV, "i2c2_pre_div", "periph_sys", 0x14c, 7),
+ DIV(PERIPH_CLK_I2C2_DIV, "i2c2_div", "i2c2_pre_div", 0x150, 7),
+ DIV(PERIPH_CLK_I2C3_PRE_DIV, "i2c3_pre_div", "periph_sys", 0x154, 7),
+ DIV(PERIPH_CLK_I2C3_DIV, "i2c3_div", "i2c3_pre_div", 0x158, 7),
+};
+
+static void __init pistachio_clk_periph_init(struct device_node *np)
+{
+ struct pistachio_clk_provider *p;
+
+ p = pistachio_clk_alloc_provider(np, PERIPH_CLK_NR_CLKS);
+ if (!p)
+ return;
+
+ pistachio_clk_register_div(p, pistachio_periph_divs,
+ ARRAY_SIZE(pistachio_periph_divs));
+ pistachio_clk_register_gate(p, pistachio_periph_gates,
+ ARRAY_SIZE(pistachio_periph_gates));
+
+ pistachio_clk_register_provider(p);
+}
+CLK_OF_DECLARE(pistachio_clk_periph, "img,pistachio-clk-periph",
+ pistachio_clk_periph_init);
+
+static struct pistachio_gate pistachio_sys_gates[] __initdata = {
+ GATE(SYS_CLK_I2C0, "i2c0_sys", "sys", 0x8, 0),
+ GATE(SYS_CLK_I2C1, "i2c1_sys", "sys", 0x8, 1),
+ GATE(SYS_CLK_I2C2, "i2c2_sys", "sys", 0x8, 2),
+ GATE(SYS_CLK_I2C3, "i2c3_sys", "sys", 0x8, 3),
+ GATE(SYS_CLK_I2S_IN, "i2s_in_sys", "sys", 0x8, 4),
+ GATE(SYS_CLK_PAUD_OUT, "paud_out_sys", "sys", 0x8, 5),
+ GATE(SYS_CLK_SPDIF_OUT, "spdif_out_sys", "sys", 0x8, 6),
+ GATE(SYS_CLK_SPI0_MASTER, "spi0_master_sys", "sys", 0x8, 7),
+ GATE(SYS_CLK_SPI0_SLAVE, "spi0_slave_sys", "sys", 0x8, 8),
+ GATE(SYS_CLK_PWM, "pwm_sys", "sys", 0x8, 9),
+ GATE(SYS_CLK_UART0, "uart0_sys", "sys", 0x8, 10),
+ GATE(SYS_CLK_UART1, "uart1_sys", "sys", 0x8, 11),
+ GATE(SYS_CLK_SPI1, "spi1_sys", "sys", 0x8, 12),
+ GATE(SYS_CLK_MDC, "mdc_sys", "sys", 0x8, 13),
+ GATE(SYS_CLK_SD_HOST, "sd_host_sys", "sys", 0x8, 14),
+ GATE(SYS_CLK_ENET, "enet_sys", "sys", 0x8, 15),
+ GATE(SYS_CLK_IR, "ir_sys", "sys", 0x8, 16),
+ GATE(SYS_CLK_WD, "wd_sys", "sys", 0x8, 17),
+ GATE(SYS_CLK_TIMER, "timer_sys", "sys", 0x8, 18),
+ GATE(SYS_CLK_I2S_OUT, "i2s_out_sys", "sys", 0x8, 24),
+ GATE(SYS_CLK_SPDIF_IN, "spdif_in_sys", "sys", 0x8, 25),
+ GATE(SYS_CLK_EVENT_TIMER, "event_timer_sys", "sys", 0x8, 26),
+ GATE(SYS_CLK_HASH, "hash_sys", "sys", 0x8, 27),
+};
+
+static void __init pistachio_cr_periph_init(struct device_node *np)
+{
+ struct pistachio_clk_provider *p;
+
+ p = pistachio_clk_alloc_provider(np, SYS_CLK_NR_CLKS);
+ if (!p)
+ return;
+
+ pistachio_clk_register_gate(p, pistachio_sys_gates,
+ ARRAY_SIZE(pistachio_sys_gates));
+
+ pistachio_clk_register_provider(p);
+}
+CLK_OF_DECLARE(pistachio_cr_periph, "img,pistachio-cr-periph",
+ pistachio_cr_periph_init);
+
+static struct pistachio_gate pistachio_ext_gates[] __initdata = {
+ GATE(EXT_CLK_ENET_IN, "enet_clk_in_gate", "enet_clk_in", 0x58, 5),
+ GATE(EXT_CLK_AUDIO_IN, "audio_clk_in_gate", "audio_clk_in", 0x58, 8)
+};
+
+static void __init pistachio_cr_top_init(struct device_node *np)
+{
+ struct pistachio_clk_provider *p;
+
+ p = pistachio_clk_alloc_provider(np, EXT_CLK_NR_CLKS);
+ if (!p)
+ return;
+
+ pistachio_clk_register_gate(p, pistachio_ext_gates,
+ ARRAY_SIZE(pistachio_ext_gates));
+
+ pistachio_clk_register_provider(p);
+}
+CLK_OF_DECLARE(pistachio_cr_top, "img,pistachio-cr-top",
+ pistachio_cr_top_init);
diff --git a/drivers/clk/pistachio/clk-pll.c b/drivers/clk/pistachio/clk-pll.c
new file mode 100644
index 000000000000..de537560bf70
--- /dev/null
+++ b/drivers/clk/pistachio/clk-pll.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include "clk.h"
+
+#define PLL_STATUS 0x0
+#define PLL_STATUS_LOCK BIT(0)
+
+#define PLL_CTRL1 0x4
+#define PLL_CTRL1_REFDIV_SHIFT 0
+#define PLL_CTRL1_REFDIV_MASK 0x3f
+#define PLL_CTRL1_FBDIV_SHIFT 6
+#define PLL_CTRL1_FBDIV_MASK 0xfff
+#define PLL_INT_CTRL1_POSTDIV1_SHIFT 18
+#define PLL_INT_CTRL1_POSTDIV1_MASK 0x7
+#define PLL_INT_CTRL1_POSTDIV2_SHIFT 21
+#define PLL_INT_CTRL1_POSTDIV2_MASK 0x7
+#define PLL_INT_CTRL1_PD BIT(24)
+#define PLL_INT_CTRL1_DSMPD BIT(25)
+#define PLL_INT_CTRL1_FOUTPOSTDIVPD BIT(26)
+#define PLL_INT_CTRL1_FOUTVCOPD BIT(27)
+
+#define PLL_CTRL2 0x8
+#define PLL_FRAC_CTRL2_FRAC_SHIFT 0
+#define PLL_FRAC_CTRL2_FRAC_MASK 0xffffff
+#define PLL_FRAC_CTRL2_POSTDIV1_SHIFT 24
+#define PLL_FRAC_CTRL2_POSTDIV1_MASK 0x7
+#define PLL_FRAC_CTRL2_POSTDIV2_SHIFT 27
+#define PLL_FRAC_CTRL2_POSTDIV2_MASK 0x7
+#define PLL_INT_CTRL2_BYPASS BIT(28)
+
+#define PLL_CTRL3 0xc
+#define PLL_FRAC_CTRL3_PD BIT(0)
+#define PLL_FRAC_CTRL3_DACPD BIT(1)
+#define PLL_FRAC_CTRL3_DSMPD BIT(2)
+#define PLL_FRAC_CTRL3_FOUTPOSTDIVPD BIT(3)
+#define PLL_FRAC_CTRL3_FOUT4PHASEPD BIT(4)
+#define PLL_FRAC_CTRL3_FOUTVCOPD BIT(5)
+
+#define PLL_CTRL4 0x10
+#define PLL_FRAC_CTRL4_BYPASS BIT(28)
+
+struct pistachio_clk_pll {
+ struct clk_hw hw;
+ void __iomem *base;
+ struct pistachio_pll_rate_table *rates;
+ unsigned int nr_rates;
+};
+
+static inline u32 pll_readl(struct pistachio_clk_pll *pll, u32 reg)
+{
+ return readl(pll->base + reg);
+}
+
+static inline void pll_writel(struct pistachio_clk_pll *pll, u32 val, u32 reg)
+{
+ writel(val, pll->base + reg);
+}
+
+static inline u32 do_div_round_closest(u64 dividend, u32 divisor)
+{
+ dividend += divisor / 2;
+ do_div(dividend, divisor);
+
+ return dividend;
+}
+
+static inline struct pistachio_clk_pll *to_pistachio_pll(struct clk_hw *hw)
+{
+ return container_of(hw, struct pistachio_clk_pll, hw);
+}
+
+static struct pistachio_pll_rate_table *
+pll_get_params(struct pistachio_clk_pll *pll, unsigned long fref,
+ unsigned long fout)
+{
+ unsigned int i;
+
+ for (i = 0; i < pll->nr_rates; i++) {
+ if (pll->rates[i].fref == fref && pll->rates[i].fout == fout)
+ return &pll->rates[i];
+ }
+
+ return NULL;
+}
+
+static long pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+ unsigned int i;
+
+ for (i = 0; i < pll->nr_rates; i++) {
+ if (i > 0 && pll->rates[i].fref == *parent_rate &&
+ pll->rates[i].fout <= rate)
+ return pll->rates[i - 1].fout;
+ }
+
+ return pll->rates[0].fout;
+}
+
+static int pll_gf40lp_frac_enable(struct clk_hw *hw)
+{
+ struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+ u32 val;
+
+ val = pll_readl(pll, PLL_CTRL3);
+ val &= ~(PLL_FRAC_CTRL3_PD | PLL_FRAC_CTRL3_DACPD |
+ PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_FOUTPOSTDIVPD |
+ PLL_FRAC_CTRL3_FOUT4PHASEPD | PLL_FRAC_CTRL3_FOUTVCOPD);
+ pll_writel(pll, val, PLL_CTRL3);
+
+ val = pll_readl(pll, PLL_CTRL4);
+ val &= ~PLL_FRAC_CTRL4_BYPASS;
+ pll_writel(pll, val, PLL_CTRL4);
+
+ return 0;
+}
+
+static void pll_gf40lp_frac_disable(struct clk_hw *hw)
+{
+ struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+ u32 val;
+
+ val = pll_readl(pll, PLL_CTRL3);
+ val |= PLL_FRAC_CTRL3_PD;
+ pll_writel(pll, val, PLL_CTRL3);
+}
+
+static int pll_gf40lp_frac_is_enabled(struct clk_hw *hw)
+{
+ struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+
+ return !(pll_readl(pll, PLL_CTRL3) & PLL_FRAC_CTRL3_PD);
+}
+
+static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+ struct pistachio_pll_rate_table *params;
+ bool was_enabled;
+ u32 val;
+
+ params = pll_get_params(pll, parent_rate, rate);
+ if (!params)
+ return -EINVAL;
+
+ was_enabled = pll_gf40lp_frac_is_enabled(hw);
+ if (!was_enabled)
+ pll_gf40lp_frac_enable(hw);
+
+ val = pll_readl(pll, PLL_CTRL1);
+ val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) |
+ (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT));
+ val |= (params->refdiv << PLL_CTRL1_REFDIV_SHIFT) |
+ (params->fbdiv << PLL_CTRL1_FBDIV_SHIFT);
+ pll_writel(pll, val, PLL_CTRL1);
+
+ val = pll_readl(pll, PLL_CTRL2);
+ val &= ~((PLL_FRAC_CTRL2_FRAC_MASK << PLL_FRAC_CTRL2_FRAC_SHIFT) |
+ (PLL_FRAC_CTRL2_POSTDIV1_MASK <<
+ PLL_FRAC_CTRL2_POSTDIV1_SHIFT) |
+ (PLL_FRAC_CTRL2_POSTDIV2_MASK <<
+ PLL_FRAC_CTRL2_POSTDIV2_SHIFT));
+ val |= (params->frac << PLL_FRAC_CTRL2_FRAC_SHIFT) |
+ (params->postdiv1 << PLL_FRAC_CTRL2_POSTDIV1_SHIFT) |
+ (params->postdiv2 << PLL_FRAC_CTRL2_POSTDIV2_SHIFT);
+ pll_writel(pll, val, PLL_CTRL2);
+
+ while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK))
+ cpu_relax();
+
+ if (!was_enabled)
+ pll_gf40lp_frac_disable(hw);
+
+ return 0;
+}
+
+static unsigned long pll_gf40lp_frac_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+ u32 val, prediv, fbdiv, frac, postdiv1, postdiv2;
+ u64 rate = parent_rate;
+
+ val = pll_readl(pll, PLL_CTRL1);
+ prediv = (val >> PLL_CTRL1_REFDIV_SHIFT) & PLL_CTRL1_REFDIV_MASK;
+ fbdiv = (val >> PLL_CTRL1_FBDIV_SHIFT) & PLL_CTRL1_FBDIV_MASK;
+
+ val = pll_readl(pll, PLL_CTRL2);
+ postdiv1 = (val >> PLL_FRAC_CTRL2_POSTDIV1_SHIFT) &
+ PLL_FRAC_CTRL2_POSTDIV1_MASK;
+ postdiv2 = (val >> PLL_FRAC_CTRL2_POSTDIV2_SHIFT) &
+ PLL_FRAC_CTRL2_POSTDIV2_MASK;
+ frac = (val >> PLL_FRAC_CTRL2_FRAC_SHIFT) & PLL_FRAC_CTRL2_FRAC_MASK;
+
+ rate *= (fbdiv << 24) + frac;
+ rate = do_div_round_closest(rate, (prediv * postdiv1 * postdiv2) << 24);
+
+ return rate;
+}
+
+static struct clk_ops pll_gf40lp_frac_ops = {
+ .enable = pll_gf40lp_frac_enable,
+ .disable = pll_gf40lp_frac_disable,
+ .is_enabled = pll_gf40lp_frac_is_enabled,
+ .recalc_rate = pll_gf40lp_frac_recalc_rate,
+ .round_rate = pll_round_rate,
+ .set_rate = pll_gf40lp_frac_set_rate,
+};
+
+static struct clk_ops pll_gf40lp_frac_fixed_ops = {
+ .enable = pll_gf40lp_frac_enable,
+ .disable = pll_gf40lp_frac_disable,
+ .is_enabled = pll_gf40lp_frac_is_enabled,
+ .recalc_rate = pll_gf40lp_frac_recalc_rate,
+};
+
+static int pll_gf40lp_laint_enable(struct clk_hw *hw)
+{
+ struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+ u32 val;
+
+ val = pll_readl(pll, PLL_CTRL1);
+ val &= ~(PLL_INT_CTRL1_PD | PLL_INT_CTRL1_DSMPD |
+ PLL_INT_CTRL1_FOUTPOSTDIVPD | PLL_INT_CTRL1_FOUTVCOPD);
+ pll_writel(pll, val, PLL_CTRL1);
+
+ val = pll_readl(pll, PLL_CTRL2);
+ val &= ~PLL_INT_CTRL2_BYPASS;
+ pll_writel(pll, val, PLL_CTRL2);
+
+ return 0;
+}
+
+static void pll_gf40lp_laint_disable(struct clk_hw *hw)
+{
+ struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+ u32 val;
+
+ val = pll_readl(pll, PLL_CTRL1);
+ val |= PLL_INT_CTRL1_PD;
+ pll_writel(pll, val, PLL_CTRL1);
+}
+
+static int pll_gf40lp_laint_is_enabled(struct clk_hw *hw)
+{
+ struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+
+ return !(pll_readl(pll, PLL_CTRL1) & PLL_INT_CTRL1_PD);
+}
+
+static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+ struct pistachio_pll_rate_table *params;
+ bool was_enabled;
+ u32 val;
+
+ params = pll_get_params(pll, parent_rate, rate);
+ if (!params)
+ return -EINVAL;
+
+ was_enabled = pll_gf40lp_laint_is_enabled(hw);
+ if (!was_enabled)
+ pll_gf40lp_laint_enable(hw);
+
+ val = pll_readl(pll, PLL_CTRL1);
+ val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) |
+ (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT) |
+ (PLL_INT_CTRL1_POSTDIV1_MASK << PLL_INT_CTRL1_POSTDIV1_SHIFT) |
+ (PLL_INT_CTRL1_POSTDIV2_MASK << PLL_INT_CTRL1_POSTDIV2_SHIFT));
+ val |= (params->refdiv << PLL_CTRL1_REFDIV_SHIFT) |
+ (params->fbdiv << PLL_CTRL1_FBDIV_SHIFT) |
+ (params->postdiv1 << PLL_INT_CTRL1_POSTDIV1_SHIFT) |
+ (params->postdiv2 << PLL_INT_CTRL1_POSTDIV2_SHIFT);
+ pll_writel(pll, val, PLL_CTRL1);
+
+ while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK))
+ cpu_relax();
+
+ if (!was_enabled)
+ pll_gf40lp_laint_disable(hw);
+
+ return 0;
+}
+
+static unsigned long pll_gf40lp_laint_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+ u32 val, prediv, fbdiv, postdiv1, postdiv2;
+ u64 rate = parent_rate;
+
+ val = pll_readl(pll, PLL_CTRL1);
+ prediv = (val >> PLL_CTRL1_REFDIV_SHIFT) & PLL_CTRL1_REFDIV_MASK;
+ fbdiv = (val >> PLL_CTRL1_FBDIV_SHIFT) & PLL_CTRL1_FBDIV_MASK;
+ postdiv1 = (val >> PLL_INT_CTRL1_POSTDIV1_SHIFT) &
+ PLL_INT_CTRL1_POSTDIV1_MASK;
+ postdiv2 = (val >> PLL_INT_CTRL1_POSTDIV2_SHIFT) &
+ PLL_INT_CTRL1_POSTDIV2_MASK;
+
+ rate *= fbdiv;
+ rate = do_div_round_closest(rate, prediv * postdiv1 * postdiv2);
+
+ return rate;
+}
+
+static struct clk_ops pll_gf40lp_laint_ops = {
+ .enable = pll_gf40lp_laint_enable,
+ .disable = pll_gf40lp_laint_disable,
+ .is_enabled = pll_gf40lp_laint_is_enabled,
+ .recalc_rate = pll_gf40lp_laint_recalc_rate,
+ .round_rate = pll_round_rate,
+ .set_rate = pll_gf40lp_laint_set_rate,
+};
+
+static struct clk_ops pll_gf40lp_laint_fixed_ops = {
+ .enable = pll_gf40lp_laint_enable,
+ .disable = pll_gf40lp_laint_disable,
+ .is_enabled = pll_gf40lp_laint_is_enabled,
+ .recalc_rate = pll_gf40lp_laint_recalc_rate,
+};
+
+static struct clk *pll_register(const char *name, const char *parent_name,
+ unsigned long flags, void __iomem *base,
+ enum pistachio_pll_type type,
+ struct pistachio_pll_rate_table *rates,
+ unsigned int nr_rates)
+{
+ struct pistachio_clk_pll *pll;
+ struct clk_init_data init;
+ struct clk *clk;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.flags = flags | CLK_GET_RATE_NOCACHE;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ switch (type) {
+ case PLL_GF40LP_FRAC:
+ if (rates)
+ init.ops = &pll_gf40lp_frac_ops;
+ else
+ init.ops = &pll_gf40lp_frac_fixed_ops;
+ break;
+ case PLL_GF40LP_LAINT:
+ if (rates)
+ init.ops = &pll_gf40lp_laint_ops;
+ else
+ init.ops = &pll_gf40lp_laint_fixed_ops;
+ break;
+ default:
+ pr_err("Unrecognized PLL type %u\n", type);
+ kfree(pll);
+ return ERR_PTR(-EINVAL);
+ }
+
+ pll->hw.init = &init;
+ pll->base = base;
+ pll->rates = rates;
+ pll->nr_rates = nr_rates;
+
+ clk = clk_register(NULL, &pll->hw);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+void pistachio_clk_register_pll(struct pistachio_clk_provider *p,
+ struct pistachio_pll *pll,
+ unsigned int num)
+{
+ struct clk *clk;
+ unsigned int i;
+
+ for (i = 0; i < num; i++) {
+ clk = pll_register(pll[i].name, pll[i].parent,
+ 0, p->base + pll[i].reg_base,
+ pll[i].type, pll[i].rates,
+ pll[i].nr_rates);
+ p->clk_data.clks[pll[i].id] = clk;
+ }
+}
diff --git a/drivers/clk/pistachio/clk.c b/drivers/clk/pistachio/clk.c
new file mode 100644
index 000000000000..85faa83e1bd7
--- /dev/null
+++ b/drivers/clk/pistachio/clk.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+#include "clk.h"
+
+struct pistachio_clk_provider *
+pistachio_clk_alloc_provider(struct device_node *node, unsigned int num_clks)
+{
+ struct pistachio_clk_provider *p;
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return p;
+
+ p->clk_data.clks = kcalloc(num_clks, sizeof(struct clk *), GFP_KERNEL);
+ if (!p->clk_data.clks)
+ goto free_provider;
+ p->clk_data.clk_num = num_clks;
+ p->node = node;
+ p->base = of_iomap(node, 0);
+ if (!p->base) {
+ pr_err("Failed to map clock provider registers\n");
+ goto free_clks;
+ }
+
+ return p;
+
+free_clks:
+ kfree(p->clk_data.clks);
+free_provider:
+ kfree(p);
+ return NULL;
+}
+
+void pistachio_clk_register_provider(struct pistachio_clk_provider *p)
+{
+ unsigned int i;
+
+ for (i = 0; i < p->clk_data.clk_num; i++) {
+ if (IS_ERR(p->clk_data.clks[i]))
+ pr_warn("Failed to register clock %d: %ld\n", i,
+ PTR_ERR(p->clk_data.clks[i]));
+ }
+
+ of_clk_add_provider(p->node, of_clk_src_onecell_get, &p->clk_data);
+}
+
+void pistachio_clk_register_gate(struct pistachio_clk_provider *p,
+ struct pistachio_gate *gate,
+ unsigned int num)
+{
+ struct clk *clk;
+ unsigned int i;
+
+ for (i = 0; i < num; i++) {
+ clk = clk_register_gate(NULL, gate[i].name, gate[i].parent,
+ CLK_SET_RATE_PARENT,
+ p->base + gate[i].reg, gate[i].shift,
+ 0, NULL);
+ p->clk_data.clks[gate[i].id] = clk;
+ }
+}
+
+void pistachio_clk_register_mux(struct pistachio_clk_provider *p,
+ struct pistachio_mux *mux,
+ unsigned int num)
+{
+ struct clk *clk;
+ unsigned int i;
+
+ for (i = 0; i < num; i++) {
+ clk = clk_register_mux(NULL, mux[i].name, mux[i].parents,
+ mux[i].num_parents,
+ CLK_SET_RATE_NO_REPARENT,
+ p->base + mux[i].reg, mux[i].shift,
+ get_count_order(mux[i].num_parents),
+ 0, NULL);
+ p->clk_data.clks[mux[i].id] = clk;
+ }
+}
+
+void pistachio_clk_register_div(struct pistachio_clk_provider *p,
+ struct pistachio_div *div,
+ unsigned int num)
+{
+ struct clk *clk;
+ unsigned int i;
+
+ for (i = 0; i < num; i++) {
+ clk = clk_register_divider(NULL, div[i].name, div[i].parent,
+ 0, p->base + div[i].reg, 0,
+ div[i].width, div[i].div_flags,
+ NULL);
+ p->clk_data.clks[div[i].id] = clk;
+ }
+}
+
+void pistachio_clk_register_fixed_factor(struct pistachio_clk_provider *p,
+ struct pistachio_fixed_factor *ff,
+ unsigned int num)
+{
+ struct clk *clk;
+ unsigned int i;
+
+ for (i = 0; i < num; i++) {
+ clk = clk_register_fixed_factor(NULL, ff[i].name, ff[i].parent,
+ 0, 1, ff[i].div);
+ p->clk_data.clks[ff[i].id] = clk;
+ }
+}
+
+void pistachio_clk_force_enable(struct pistachio_clk_provider *p,
+ unsigned int *clk_ids, unsigned int num)
+{
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < num; i++) {
+ struct clk *clk = p->clk_data.clks[clk_ids[i]];
+
+ if (IS_ERR(clk))
+ continue;
+
+ err = clk_prepare_enable(clk);
+ if (err)
+ pr_err("Failed to enable clock %s: %d\n",
+ __clk_get_name(clk), err);
+ }
+}
diff --git a/drivers/clk/pistachio/clk.h b/drivers/clk/pistachio/clk.h
new file mode 100644
index 000000000000..52fabbc24624
--- /dev/null
+++ b/drivers/clk/pistachio/clk.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#ifndef __PISTACHIO_CLK_H
+#define __PISTACHIO_CLK_H
+
+#include <linux/clk-provider.h>
+
+struct pistachio_gate {
+ unsigned int id;
+ unsigned long reg;
+ unsigned int shift;
+ const char *name;
+ const char *parent;
+};
+
+#define GATE(_id, _name, _pname, _reg, _shift) \
+ { \
+ .id = _id, \
+ .reg = _reg, \
+ .shift = _shift, \
+ .name = _name, \
+ .parent = _pname, \
+ }
+
+struct pistachio_mux {
+ unsigned int id;
+ unsigned long reg;
+ unsigned int shift;
+ unsigned int num_parents;
+ const char *name;
+ const char **parents;
+};
+
+#define PNAME(x) static const char *x[] __initconst
+
+#define MUX(_id, _name, _pnames, _reg, _shift) \
+ { \
+ .id = _id, \
+ .reg = _reg, \
+ .shift = _shift, \
+ .name = _name, \
+ .parents = _pnames, \
+ .num_parents = ARRAY_SIZE(_pnames) \
+ }
+
+
+struct pistachio_div {
+ unsigned int id;
+ unsigned long reg;
+ unsigned int width;
+ unsigned int div_flags;
+ const char *name;
+ const char *parent;
+};
+
+#define DIV(_id, _name, _pname, _reg, _width) \
+ { \
+ .id = _id, \
+ .reg = _reg, \
+ .width = _width, \
+ .div_flags = 0, \
+ .name = _name, \
+ .parent = _pname, \
+ }
+
+#define DIV_F(_id, _name, _pname, _reg, _width, _div_flags) \
+ { \
+ .id = _id, \
+ .reg = _reg, \
+ .width = _width, \
+ .div_flags = _div_flags, \
+ .name = _name, \
+ .parent = _pname, \
+ }
+
+struct pistachio_fixed_factor {
+ unsigned int id;
+ unsigned int div;
+ const char *name;
+ const char *parent;
+};
+
+#define FIXED_FACTOR(_id, _name, _pname, _div) \
+ { \
+ .id = _id, \
+ .div = _div, \
+ .name = _name, \
+ .parent = _pname, \
+ }
+
+struct pistachio_pll_rate_table {
+ unsigned long fref;
+ unsigned long fout;
+ unsigned int refdiv;
+ unsigned int fbdiv;
+ unsigned int postdiv1;
+ unsigned int postdiv2;
+ unsigned int frac;
+};
+
+enum pistachio_pll_type {
+ PLL_GF40LP_LAINT,
+ PLL_GF40LP_FRAC,
+};
+
+struct pistachio_pll {
+ unsigned int id;
+ unsigned long reg_base;
+ enum pistachio_pll_type type;
+ struct pistachio_pll_rate_table *rates;
+ unsigned int nr_rates;
+ const char *name;
+ const char *parent;
+};
+
+#define PLL(_id, _name, _pname, _type, _reg, _rates) \
+ { \
+ .id = _id, \
+ .reg_base = _reg, \
+ .type = _type, \
+ .rates = _rates, \
+ .nr_rates = ARRAY_SIZE(_rates), \
+ .name = _name, \
+ .parent = _pname, \
+ }
+
+#define PLL_FIXED(_id, _name, _pname, _type, _reg) \
+ { \
+ .id = _id, \
+ .reg_base = _reg, \
+ .type = _type, \
+ .rates = NULL, \
+ .nr_rates = 0, \
+ .name = _name, \
+ .parent = _pname, \
+ }
+
+struct pistachio_clk_provider {
+ struct device_node *node;
+ void __iomem *base;
+ struct clk_onecell_data clk_data;
+};
+
+extern struct pistachio_clk_provider *
+pistachio_clk_alloc_provider(struct device_node *node, unsigned int num_clks);
+extern void pistachio_clk_register_provider(struct pistachio_clk_provider *p);
+
+extern void pistachio_clk_register_gate(struct pistachio_clk_provider *p,
+ struct pistachio_gate *gate,
+ unsigned int num);
+extern void pistachio_clk_register_mux(struct pistachio_clk_provider *p,
+ struct pistachio_mux *mux,
+ unsigned int num);
+extern void pistachio_clk_register_div(struct pistachio_clk_provider *p,
+ struct pistachio_div *div,
+ unsigned int num);
+extern void
+pistachio_clk_register_fixed_factor(struct pistachio_clk_provider *p,
+ struct pistachio_fixed_factor *ff,
+ unsigned int num);
+extern void pistachio_clk_register_pll(struct pistachio_clk_provider *p,
+ struct pistachio_pll *pll,
+ unsigned int num);
+
+extern void pistachio_clk_force_enable(struct pistachio_clk_provider *p,
+ unsigned int *clk_ids, unsigned int num);
+
+#endif
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index a0b036ccb118..b4ac7cfae441 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -233,7 +233,7 @@ config CLKSRC_QCOM
config CLKSRC_VERSATILE
bool "ARM Versatile (Express) reference platforms clock source"
- depends on GENERIC_SCHED_CLOCK && !ARCH_USES_GETTIMEOFFSET
+ depends on PLAT_VERSATILE && GENERIC_SCHED_CLOCK && !ARCH_USES_GETTIMEOFFSET
select CLKSRC_OF
default y if MFD_VEXPRESS_SYSREG
help
diff --git a/drivers/clocksource/mips-gic-timer.c b/drivers/clocksource/mips-gic-timer.c
index 3bd31b1321f6..b81ed1a5342d 100644
--- a/drivers/clocksource/mips-gic-timer.c
+++ b/drivers/clocksource/mips-gic-timer.c
@@ -5,6 +5,7 @@
*
* Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved.
*/
+#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/cpu.h>
#include <linux/init.h>
@@ -133,6 +134,9 @@ static void __init __gic_clocksource_init(void)
clocksource_register_hz(&gic_clocksource, gic_frequency);
gic_clockevent_init();
+
+ /* And finally start the counter */
+ gic_start_count();
}
void __init gic_clocksource_init(unsigned int frequency)
@@ -146,11 +150,18 @@ void __init gic_clocksource_init(unsigned int frequency)
static void __init gic_clocksource_of_init(struct device_node *node)
{
+ struct clk *clk;
+
if (WARN_ON(!gic_present || !node->parent ||
!of_device_is_compatible(node->parent, "mti,gic")))
return;
- if (of_property_read_u32(node, "clock-frequency", &gic_frequency)) {
+ clk = of_clk_get(node, 0);
+ if (!IS_ERR(clk)) {
+ gic_frequency = clk_get_rate(clk);
+ clk_put(clk);
+ } else if (of_property_read_u32(node, "clock-frequency",
+ &gic_frequency)) {
pr_err("GIC frequency not specified.\n");
return;
}
diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c
index 6791fd16272c..3ef0cf9f5c44 100644
--- a/drivers/infiniband/hw/cxgb4/mem.c
+++ b/drivers/infiniband/hw/cxgb4/mem.c
@@ -73,7 +73,7 @@ static int _c4iw_write_mem_dma_aligned(struct c4iw_rdev *rdev, u32 addr,
c4iw_init_wr_wait(&wr_wait);
wr_len = roundup(sizeof(*req) + sizeof(*sgl), 16);
- skb = alloc_skb(wr_len, GFP_KERNEL | __GFP_NOFAIL);
+ skb = alloc_skb(wr_len, GFP_KERNEL);
if (!skb)
return -ENOMEM;
set_wr_txq(skb, CPL_PRIORITY_CONTROL, 0);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 657b89b1d291..915ad04a827e 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -846,6 +846,11 @@ static int ipoib_get_iflink(const struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
+ /* parent interface */
+ if (!test_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags))
+ return dev->ifindex;
+
+ /* child/vlan interface */
return priv->parent->ifindex;
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c
index 4dd1313056a4..fca1a882de27 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c
@@ -58,6 +58,7 @@ int __ipoib_vlan_add(struct ipoib_dev_priv *ppriv, struct ipoib_dev_priv *priv,
/* MTU will be reset when mcast join happens */
priv->dev->mtu = IPOIB_UD_MTU(priv->max_ib_mtu);
priv->mcast_mtu = priv->admin_mtu = priv->dev->mtu;
+ priv->parent = ppriv->dev;
set_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags);
result = ipoib_set_dev_features(priv, ppriv->ca);
@@ -84,8 +85,6 @@ int __ipoib_vlan_add(struct ipoib_dev_priv *ppriv, struct ipoib_dev_priv *priv,
goto register_failed;
}
- priv->parent = ppriv->dev;
-
ipoib_create_debug_files(priv->dev);
/* RTNL childs don't need proprietary sysfs entries */
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index c8d260e33a90..6de62a96e79c 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -60,6 +60,11 @@ config ATMEL_AIC5_IRQ
select MULTI_IRQ_HANDLER
select SPARSE_IRQ
+config BCM7038_L1_IRQ
+ bool
+ select GENERIC_IRQ_CHIP
+ select IRQ_DOMAIN
+
config BCM7120_L2_IRQ
bool
select GENERIC_IRQ_CHIP
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 552a74027601..dda4927e47a6 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o
obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o
obj-$(CONFIG_SOC_VF610) += irq-vf610-mscm-ir.o
+obj-$(CONFIG_BCM7038_L1_IRQ) += irq-bcm7038-l1.o
obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o
obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o
obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o
diff --git a/drivers/irqchip/irq-bcm7038-l1.c b/drivers/irqchip/irq-bcm7038-l1.c
new file mode 100644
index 000000000000..d3b8c8be15f6
--- /dev/null
+++ b/drivers/irqchip/irq-bcm7038-l1.c
@@ -0,0 +1,335 @@
+/*
+ * Broadcom BCM7038 style Level 1 interrupt controller driver
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ * Author: Kevin Cernekee
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitops.h>
+#include <linux/kconfig.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/types.h>
+#include <linux/irqchip/chained_irq.h>
+
+#include "irqchip.h"
+
+#define IRQS_PER_WORD 32
+#define REG_BYTES_PER_IRQ_WORD (sizeof(u32) * 4)
+#define MAX_WORDS 8
+
+struct bcm7038_l1_cpu;
+
+struct bcm7038_l1_chip {
+ raw_spinlock_t lock;
+ unsigned int n_words;
+ struct irq_domain *domain;
+ struct bcm7038_l1_cpu *cpus[NR_CPUS];
+ u8 affinity[MAX_WORDS * IRQS_PER_WORD];
+};
+
+struct bcm7038_l1_cpu {
+ void __iomem *map_base;
+ u32 mask_cache[0];
+};
+
+/*
+ * STATUS/MASK_STATUS/MASK_SET/MASK_CLEAR are packed one right after another:
+ *
+ * 7038:
+ * 0x1000_1400: W0_STATUS
+ * 0x1000_1404: W1_STATUS
+ * 0x1000_1408: W0_MASK_STATUS
+ * 0x1000_140c: W1_MASK_STATUS
+ * 0x1000_1410: W0_MASK_SET
+ * 0x1000_1414: W1_MASK_SET
+ * 0x1000_1418: W0_MASK_CLEAR
+ * 0x1000_141c: W1_MASK_CLEAR
+ *
+ * 7445:
+ * 0xf03e_1500: W0_STATUS
+ * 0xf03e_1504: W1_STATUS
+ * 0xf03e_1508: W2_STATUS
+ * 0xf03e_150c: W3_STATUS
+ * 0xf03e_1510: W4_STATUS
+ * 0xf03e_1514: W0_MASK_STATUS
+ * 0xf03e_1518: W1_MASK_STATUS
+ * [...]
+ */
+
+static inline unsigned int reg_status(struct bcm7038_l1_chip *intc,
+ unsigned int word)
+{
+ return (0 * intc->n_words + word) * sizeof(u32);
+}
+
+static inline unsigned int reg_mask_status(struct bcm7038_l1_chip *intc,
+ unsigned int word)
+{
+ return (1 * intc->n_words + word) * sizeof(u32);
+}
+
+static inline unsigned int reg_mask_set(struct bcm7038_l1_chip *intc,
+ unsigned int word)
+{
+ return (2 * intc->n_words + word) * sizeof(u32);
+}
+
+static inline unsigned int reg_mask_clr(struct bcm7038_l1_chip *intc,
+ unsigned int word)
+{
+ return (3 * intc->n_words + word) * sizeof(u32);
+}
+
+static inline u32 l1_readl(void __iomem *reg)
+{
+ if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
+ return ioread32be(reg);
+ else
+ return readl(reg);
+}
+
+static inline void l1_writel(u32 val, void __iomem *reg)
+{
+ if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
+ iowrite32be(val, reg);
+ else
+ writel(val, reg);
+}
+
+static void bcm7038_l1_irq_handle(unsigned int irq, struct irq_desc *desc)
+{
+ struct bcm7038_l1_chip *intc = irq_desc_get_handler_data(desc);
+ struct bcm7038_l1_cpu *cpu;
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ unsigned int idx;
+
+#ifdef CONFIG_SMP
+ cpu = intc->cpus[cpu_logical_map(smp_processor_id())];
+#else
+ cpu = intc->cpus[0];
+#endif
+
+ chained_irq_enter(chip, desc);
+
+ for (idx = 0; idx < intc->n_words; idx++) {
+ int base = idx * IRQS_PER_WORD;
+ unsigned long pending, flags;
+ int hwirq;
+
+ raw_spin_lock_irqsave(&intc->lock, flags);
+ pending = l1_readl(cpu->map_base + reg_status(intc, idx)) &
+ ~cpu->mask_cache[idx];
+ raw_spin_unlock_irqrestore(&intc->lock, flags);
+
+ for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) {
+ generic_handle_irq(irq_find_mapping(intc->domain,
+ base + hwirq));
+ }
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static void __bcm7038_l1_unmask(struct irq_data *d, unsigned int cpu_idx)
+{
+ struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d);
+ u32 word = d->hwirq / IRQS_PER_WORD;
+ u32 mask = BIT(d->hwirq % IRQS_PER_WORD);
+
+ intc->cpus[cpu_idx]->mask_cache[word] &= ~mask;
+ l1_writel(mask, intc->cpus[cpu_idx]->map_base +
+ reg_mask_clr(intc, word));
+}
+
+static void __bcm7038_l1_mask(struct irq_data *d, unsigned int cpu_idx)
+{
+ struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d);
+ u32 word = d->hwirq / IRQS_PER_WORD;
+ u32 mask = BIT(d->hwirq % IRQS_PER_WORD);
+
+ intc->cpus[cpu_idx]->mask_cache[word] |= mask;
+ l1_writel(mask, intc->cpus[cpu_idx]->map_base +
+ reg_mask_set(intc, word));
+}
+
+static void bcm7038_l1_unmask(struct irq_data *d)
+{
+ struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d);
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&intc->lock, flags);
+ __bcm7038_l1_unmask(d, intc->affinity[d->hwirq]);
+ raw_spin_unlock_irqrestore(&intc->lock, flags);
+}
+
+static void bcm7038_l1_mask(struct irq_data *d)
+{
+ struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d);
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&intc->lock, flags);
+ __bcm7038_l1_mask(d, intc->affinity[d->hwirq]);
+ raw_spin_unlock_irqrestore(&intc->lock, flags);
+}
+
+static int bcm7038_l1_set_affinity(struct irq_data *d,
+ const struct cpumask *dest,
+ bool force)
+{
+ struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d);
+ unsigned long flags;
+ irq_hw_number_t hw = d->hwirq;
+ u32 word = hw / IRQS_PER_WORD;
+ u32 mask = BIT(hw % IRQS_PER_WORD);
+ unsigned int first_cpu = cpumask_any_and(dest, cpu_online_mask);
+ bool was_disabled;
+
+ raw_spin_lock_irqsave(&intc->lock, flags);
+
+ was_disabled = !!(intc->cpus[intc->affinity[hw]]->mask_cache[word] &
+ mask);
+ __bcm7038_l1_mask(d, intc->affinity[hw]);
+ intc->affinity[hw] = first_cpu;
+ if (!was_disabled)
+ __bcm7038_l1_unmask(d, first_cpu);
+
+ raw_spin_unlock_irqrestore(&intc->lock, flags);
+ return 0;
+}
+
+static int __init bcm7038_l1_init_one(struct device_node *dn,
+ unsigned int idx,
+ struct bcm7038_l1_chip *intc)
+{
+ struct resource res;
+ resource_size_t sz;
+ struct bcm7038_l1_cpu *cpu;
+ unsigned int i, n_words, parent_irq;
+
+ if (of_address_to_resource(dn, idx, &res))
+ return -EINVAL;
+ sz = resource_size(&res);
+ n_words = sz / REG_BYTES_PER_IRQ_WORD;
+
+ if (n_words > MAX_WORDS)
+ return -EINVAL;
+ else if (!intc->n_words)
+ intc->n_words = n_words;
+ else if (intc->n_words != n_words)
+ return -EINVAL;
+
+ cpu = intc->cpus[idx] = kzalloc(sizeof(*cpu) + n_words * sizeof(u32),
+ GFP_KERNEL);
+ if (!cpu)
+ return -ENOMEM;
+
+ cpu->map_base = ioremap(res.start, sz);
+ if (!cpu->map_base)
+ return -ENOMEM;
+
+ for (i = 0; i < n_words; i++) {
+ l1_writel(0xffffffff, cpu->map_base + reg_mask_set(intc, i));
+ cpu->mask_cache[i] = 0xffffffff;
+ }
+
+ parent_irq = irq_of_parse_and_map(dn, idx);
+ if (!parent_irq) {
+ pr_err("failed to map parent interrupt %d\n", parent_irq);
+ return -EINVAL;
+ }
+ irq_set_handler_data(parent_irq, intc);
+ irq_set_chained_handler(parent_irq, bcm7038_l1_irq_handle);
+
+ return 0;
+}
+
+static struct irq_chip bcm7038_l1_irq_chip = {
+ .name = "bcm7038-l1",
+ .irq_mask = bcm7038_l1_mask,
+ .irq_unmask = bcm7038_l1_unmask,
+ .irq_set_affinity = bcm7038_l1_set_affinity,
+};
+
+static int bcm7038_l1_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hw_irq)
+{
+ irq_set_chip_and_handler(virq, &bcm7038_l1_irq_chip, handle_level_irq);
+ irq_set_chip_data(virq, d->host_data);
+ return 0;
+}
+
+static const struct irq_domain_ops bcm7038_l1_domain_ops = {
+ .xlate = irq_domain_xlate_onecell,
+ .map = bcm7038_l1_map,
+};
+
+int __init bcm7038_l1_of_init(struct device_node *dn,
+ struct device_node *parent)
+{
+ struct bcm7038_l1_chip *intc;
+ int idx, ret;
+
+ intc = kzalloc(sizeof(*intc), GFP_KERNEL);
+ if (!intc)
+ return -ENOMEM;
+
+ raw_spin_lock_init(&intc->lock);
+ for_each_possible_cpu(idx) {
+ ret = bcm7038_l1_init_one(dn, idx, intc);
+ if (ret < 0) {
+ if (idx)
+ break;
+ pr_err("failed to remap intc L1 registers\n");
+ goto out_free;
+ }
+ }
+
+ intc->domain = irq_domain_add_linear(dn, IRQS_PER_WORD * intc->n_words,
+ &bcm7038_l1_domain_ops,
+ intc);
+ if (!intc->domain) {
+ ret = -ENOMEM;
+ goto out_unmap;
+ }
+
+ pr_info("registered BCM7038 L1 intc (mem: 0x%p, IRQs: %d)\n",
+ intc->cpus[0]->map_base, IRQS_PER_WORD * intc->n_words);
+
+ return 0;
+
+out_unmap:
+ for_each_possible_cpu(idx) {
+ struct bcm7038_l1_cpu *cpu = intc->cpus[idx];
+
+ if (cpu) {
+ if (cpu->map_base)
+ iounmap(cpu->map_base);
+ kfree(cpu);
+ }
+ }
+out_free:
+ kfree(intc);
+ return ret;
+}
+
+IRQCHIP_DECLARE(bcm7038_l1, "brcm,bcm7038-l1-intc", bcm7038_l1_of_init);
diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c
index 8eec8e1201d9..3ba5cc780fcb 100644
--- a/drivers/irqchip/irq-bcm7120-l2.c
+++ b/drivers/irqchip/irq-bcm7120-l2.c
@@ -14,6 +14,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kconfig.h>
+#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_irq.h>
@@ -34,15 +35,21 @@
#define IRQSTAT 0x04
#define MAX_WORDS 4
+#define MAX_MAPPINGS (MAX_WORDS * 2)
#define IRQS_PER_WORD 32
struct bcm7120_l2_intc_data {
unsigned int n_words;
- void __iomem *base[MAX_WORDS];
+ void __iomem *map_base[MAX_MAPPINGS];
+ void __iomem *pair_base[MAX_WORDS];
+ int en_offset[MAX_WORDS];
+ int stat_offset[MAX_WORDS];
struct irq_domain *domain;
bool can_wake;
u32 irq_fwd_mask[MAX_WORDS];
u32 irq_map_mask[MAX_WORDS];
+ int num_parent_irqs;
+ const __be32 *map_mask_prop;
};
static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
@@ -61,7 +68,8 @@ static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
int hwirq;
irq_gc_lock(gc);
- pending = irq_reg_readl(gc, IRQSTAT) & gc->mask_cache;
+ pending = irq_reg_readl(gc, b->stat_offset[idx]) &
+ gc->mask_cache;
irq_gc_unlock(gc);
for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) {
@@ -76,27 +84,30 @@ static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
static void bcm7120_l2_intc_suspend(struct irq_data *d)
{
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct irq_chip_type *ct = irq_data_get_chip_type(d);
struct bcm7120_l2_intc_data *b = gc->private;
irq_gc_lock(gc);
if (b->can_wake)
- irq_reg_writel(gc, gc->mask_cache | gc->wake_active, IRQEN);
+ irq_reg_writel(gc, gc->mask_cache | gc->wake_active,
+ ct->regs.mask);
irq_gc_unlock(gc);
}
static void bcm7120_l2_intc_resume(struct irq_data *d)
{
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct irq_chip_type *ct = irq_data_get_chip_type(d);
/* Restore the saved mask */
irq_gc_lock(gc);
- irq_reg_writel(gc, gc->mask_cache, IRQEN);
+ irq_reg_writel(gc, gc->mask_cache, ct->regs.mask);
irq_gc_unlock(gc);
}
static int bcm7120_l2_intc_init_one(struct device_node *dn,
struct bcm7120_l2_intc_data *data,
- int irq, const __be32 *map_mask)
+ int irq)
{
int parent_irq;
unsigned int idx;
@@ -110,9 +121,15 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn,
/* For multiple parent IRQs with multiple words, this looks like:
* <irq0_w0 irq0_w1 irq1_w0 irq1_w1 ...>
*/
- for (idx = 0; idx < data->n_words; idx++)
- data->irq_map_mask[idx] |=
- be32_to_cpup(map_mask + irq * data->n_words + idx);
+ for (idx = 0; idx < data->n_words; idx++) {
+ if (data->map_mask_prop) {
+ data->irq_map_mask[idx] |=
+ be32_to_cpup(data->map_mask_prop +
+ irq * data->n_words + idx);
+ } else {
+ data->irq_map_mask[idx] = 0xffffffff;
+ }
+ }
irq_set_handler_data(parent_irq, data);
irq_set_chained_handler(parent_irq, bcm7120_l2_intc_irq_handle);
@@ -120,68 +137,107 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn,
return 0;
}
-int __init bcm7120_l2_intc_of_init(struct device_node *dn,
- struct device_node *parent)
+static int __init bcm7120_l2_intc_iomap_7120(struct device_node *dn,
+ struct bcm7120_l2_intc_data *data)
+{
+ int ret;
+
+ data->map_base[0] = of_iomap(dn, 0);
+ if (!data->map_base[0]) {
+ pr_err("unable to map registers\n");
+ return -ENOMEM;
+ }
+
+ data->pair_base[0] = data->map_base[0];
+ data->en_offset[0] = IRQEN;
+ data->stat_offset[0] = IRQSTAT;
+ data->n_words = 1;
+
+ ret = of_property_read_u32_array(dn, "brcm,int-fwd-mask",
+ data->irq_fwd_mask, data->n_words);
+ if (ret != 0 && ret != -EINVAL) {
+ /* property exists but has the wrong number of words */
+ pr_err("invalid brcm,int-fwd-mask property\n");
+ return -EINVAL;
+ }
+
+ data->map_mask_prop = of_get_property(dn, "brcm,int-map-mask", &ret);
+ if (!data->map_mask_prop ||
+ (ret != (sizeof(__be32) * data->num_parent_irqs * data->n_words))) {
+ pr_err("invalid brcm,int-map-mask property\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __init bcm7120_l2_intc_iomap_3380(struct device_node *dn,
+ struct bcm7120_l2_intc_data *data)
+{
+ unsigned int gc_idx;
+
+ for (gc_idx = 0; gc_idx < MAX_WORDS; gc_idx++) {
+ unsigned int map_idx = gc_idx * 2;
+ void __iomem *en = of_iomap(dn, map_idx + 0);
+ void __iomem *stat = of_iomap(dn, map_idx + 1);
+ void __iomem *base = min(en, stat);
+
+ data->map_base[map_idx + 0] = en;
+ data->map_base[map_idx + 1] = stat;
+
+ if (!base)
+ break;
+
+ data->pair_base[gc_idx] = base;
+ data->en_offset[gc_idx] = en - base;
+ data->stat_offset[gc_idx] = stat - base;
+ }
+
+ if (!gc_idx) {
+ pr_err("unable to map registers\n");
+ return -EINVAL;
+ }
+
+ data->n_words = gc_idx;
+ return 0;
+}
+
+int __init bcm7120_l2_intc_probe(struct device_node *dn,
+ struct device_node *parent,
+ int (*iomap_regs_fn)(struct device_node *,
+ struct bcm7120_l2_intc_data *),
+ const char *intc_name)
{
unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
struct bcm7120_l2_intc_data *data;
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
- const __be32 *map_mask;
- int num_parent_irqs;
- int ret = 0, len;
+ int ret = 0;
unsigned int idx, irq, flags;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
- for (idx = 0; idx < MAX_WORDS; idx++) {
- data->base[idx] = of_iomap(dn, idx);
- if (!data->base[idx])
- break;
- data->n_words = idx + 1;
- }
- if (!data->n_words) {
- pr_err("failed to remap intc L2 registers\n");
- ret = -ENOMEM;
- goto out_unmap;
- }
-
- /* Enable all interrupts specified in the interrupt forward mask;
- * disable all others. If the property doesn't exist (-EINVAL),
- * assume all zeroes.
- */
- ret = of_property_read_u32_array(dn, "brcm,int-fwd-mask",
- data->irq_fwd_mask, data->n_words);
- if (ret == 0 || ret == -EINVAL) {
- for (idx = 0; idx < data->n_words; idx++)
- __raw_writel(data->irq_fwd_mask[idx],
- data->base[idx] + IRQEN);
- } else {
- /* property exists but has the wrong number of words */
- pr_err("invalid int-fwd-mask property\n");
- ret = -EINVAL;
- goto out_unmap;
- }
-
- num_parent_irqs = of_irq_count(dn);
- if (num_parent_irqs <= 0) {
+ data->num_parent_irqs = of_irq_count(dn);
+ if (data->num_parent_irqs <= 0) {
pr_err("invalid number of parent interrupts\n");
ret = -ENOMEM;
goto out_unmap;
}
- map_mask = of_get_property(dn, "brcm,int-map-mask", &len);
- if (!map_mask ||
- (len != (sizeof(*map_mask) * num_parent_irqs * data->n_words))) {
- pr_err("invalid brcm,int-map-mask property\n");
- ret = -EINVAL;
+ ret = iomap_regs_fn(dn, data);
+ if (ret < 0)
goto out_unmap;
+
+ for (idx = 0; idx < data->n_words; idx++) {
+ __raw_writel(data->irq_fwd_mask[idx],
+ data->pair_base[idx] +
+ data->en_offset[idx]);
}
- for (irq = 0; irq < num_parent_irqs; irq++) {
- ret = bcm7120_l2_intc_init_one(dn, data, irq, map_mask);
+ for (irq = 0; irq < data->num_parent_irqs; irq++) {
+ ret = bcm7120_l2_intc_init_one(dn, data, irq);
if (ret)
goto out_unmap;
}
@@ -215,11 +271,12 @@ int __init bcm7120_l2_intc_of_init(struct device_node *dn,
gc = irq_get_domain_generic_chip(data->domain, irq);
gc->unused = 0xffffffff & ~data->irq_map_mask[idx];
- gc->reg_base = data->base[idx];
gc->private = data;
ct = gc->chip_types;
- ct->regs.mask = IRQEN;
+ gc->reg_base = data->pair_base[idx];
+ ct->regs.mask = data->en_offset[idx];
+
ct->chip.irq_mask = irq_gc_mask_clr_bit;
ct->chip.irq_unmask = irq_gc_mask_set_bit;
ct->chip.irq_ack = irq_gc_noop;
@@ -236,20 +293,38 @@ int __init bcm7120_l2_intc_of_init(struct device_node *dn,
}
}
- pr_info("registered BCM7120 L2 intc (mem: 0x%p, parent IRQ(s): %d)\n",
- data->base[0], num_parent_irqs);
+ pr_info("registered %s intc (mem: 0x%p, parent IRQ(s): %d)\n",
+ intc_name, data->map_base[0], data->num_parent_irqs);
return 0;
out_free_domain:
irq_domain_remove(data->domain);
out_unmap:
- for (idx = 0; idx < MAX_WORDS; idx++) {
- if (data->base[idx])
- iounmap(data->base[idx]);
+ for (idx = 0; idx < MAX_MAPPINGS; idx++) {
+ if (data->map_base[idx])
+ iounmap(data->map_base[idx]);
}
kfree(data);
return ret;
}
+
+int __init bcm7120_l2_intc_probe_7120(struct device_node *dn,
+ struct device_node *parent)
+{
+ return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_7120,
+ "BCM7120 L2");
+}
+
+int __init bcm7120_l2_intc_probe_3380(struct device_node *dn,
+ struct device_node *parent)
+{
+ return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_3380,
+ "BCM3380 L2");
+}
+
IRQCHIP_DECLARE(bcm7120_l2_intc, "brcm,bcm7120-l2-intc",
- bcm7120_l2_intc_of_init);
+ bcm7120_l2_intc_probe_7120);
+
+IRQCHIP_DECLARE(bcm3380_l2_intc, "brcm,bcm3380-l2-intc",
+ bcm7120_l2_intc_probe_3380);
diff --git a/drivers/irqchip/irq-brcmstb-l2.c b/drivers/irqchip/irq-brcmstb-l2.c
index 313c2c64498a..d6bcc6be0777 100644
--- a/drivers/irqchip/irq-brcmstb-l2.c
+++ b/drivers/irqchip/irq-brcmstb-l2.c
@@ -136,7 +136,11 @@ int __init brcmstb_l2_intc_of_init(struct device_node *np,
/* Disable all interrupts by default */
writel(0xffffffff, data->base + CPU_MASK_SET);
- writel(0xffffffff, data->base + CPU_CLEAR);
+
+ /* Wakeup interrupts may be retained from S5 (cold boot) */
+ data->can_wake = of_property_read_bool(np, "brcm,irq-can-wake");
+ if (!data->can_wake)
+ writel(0xffffffff, data->base + CPU_CLEAR);
data->parent_irq = irq_of_parse_and_map(np, 0);
if (!data->parent_irq) {
@@ -188,8 +192,7 @@ int __init brcmstb_l2_intc_of_init(struct device_node *np,
ct->chip.irq_suspend = brcmstb_l2_intc_suspend;
ct->chip.irq_resume = brcmstb_l2_intc_resume;
- if (of_property_read_bool(np, "brcm,irq-can-wake")) {
- data->can_wake = true;
+ if (data->can_wake) {
/* This IRQ chip can wake the system, set all child interrupts
* in wake_enabled mask
*/
diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c
index f2d269bca789..bc48b7dc89ec 100644
--- a/drivers/irqchip/irq-mips-gic.c
+++ b/drivers/irqchip/irq-mips-gic.c
@@ -239,7 +239,7 @@ int gic_get_c0_compare_int(void)
int gic_get_c0_perfcount_int(void)
{
if (!gic_local_irq_is_routable(GIC_LOCAL_INT_PERFCTR)) {
- /* Is the erformance counter shared with the timer? */
+ /* Is the performance counter shared with the timer? */
if (cp0_perfcount_irq < 0)
return -1;
return MIPS_CPU_IRQ_BASE + cp0_perfcount_irq;
@@ -248,6 +248,29 @@ int gic_get_c0_perfcount_int(void)
GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_PERFCTR));
}
+int gic_get_c0_fdc_int(void)
+{
+ if (!gic_local_irq_is_routable(GIC_LOCAL_INT_FDC)) {
+ /* Is the FDC IRQ even present? */
+ if (cp0_fdc_irq < 0)
+ return -1;
+ return MIPS_CPU_IRQ_BASE + cp0_fdc_irq;
+ }
+
+ /*
+ * Some cores claim the FDC is routable but it doesn't actually seem to
+ * be connected.
+ */
+ switch (current_cpu_type()) {
+ case CPU_INTERAPTIV:
+ case CPU_PROAPTIV:
+ return -1;
+ }
+
+ return irq_create_mapping(gic_irq_domain,
+ GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_FDC));
+}
+
static void gic_handle_shared_int(void)
{
unsigned int i, intr, virq;
@@ -613,15 +636,20 @@ static int gic_local_irq_domain_map(struct irq_domain *d, unsigned int virq,
* of the MIPS kernel code does not use the percpu IRQ API for
* the CP0 timer and performance counter interrupts.
*/
- if (intr != GIC_LOCAL_INT_TIMER && intr != GIC_LOCAL_INT_PERFCTR) {
+ switch (intr) {
+ case GIC_LOCAL_INT_TIMER:
+ case GIC_LOCAL_INT_PERFCTR:
+ case GIC_LOCAL_INT_FDC:
+ irq_set_chip_and_handler(virq,
+ &gic_all_vpes_local_irq_controller,
+ handle_percpu_irq);
+ break;
+ default:
irq_set_chip_and_handler(virq,
&gic_local_irq_controller,
handle_percpu_devid_irq);
irq_set_percpu_devid(virq);
- } else {
- irq_set_chip_and_handler(virq,
- &gic_all_vpes_local_irq_controller,
- handle_percpu_irq);
+ break;
}
spin_lock_irqsave(&gic_lock, flags);
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index fc8d3b6ffe8e..9f0c2b9d58ae 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -602,8 +602,6 @@ static void _mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
u32 high = 0;
if (s->reg >= 0x100) {
- int ret;
-
ret = mv88e6xxx_reg_read(ds, REG_PORT(port),
s->reg - 0x100);
if (ret < 0)
@@ -902,14 +900,16 @@ static int _mv88e6xxx_flush_fid(struct dsa_switch *ds, int fid)
static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
- int reg, ret;
+ int reg, ret = 0;
u8 oldstate;
mutex_lock(&ps->smi_mutex);
reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_CONTROL);
- if (reg < 0)
+ if (reg < 0) {
+ ret = reg;
goto abort;
+ }
oldstate = reg & PORT_CONTROL_STATE_MASK;
if (oldstate != state) {
diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c
index 79ea35869e1e..90a76306ad0f 100644
--- a/drivers/net/ethernet/altera/altera_tse_main.c
+++ b/drivers/net/ethernet/altera/altera_tse_main.c
@@ -376,8 +376,13 @@ static int tse_rx(struct altera_tse_private *priv, int limit)
u16 pktlength;
u16 pktstatus;
- while (((rxstatus = priv->dmaops->get_rx_status(priv)) != 0) &&
- (count < limit)) {
+ /* Check for count < limit first as get_rx_status is changing
+ * the response-fifo so we must process the next packet
+ * after calling get_rx_status if a response is pending.
+ * (reading the last byte of the response pops the value from the fifo.)
+ */
+ while ((count < limit) &&
+ ((rxstatus = priv->dmaops->get_rx_status(priv)) != 0)) {
pktstatus = rxstatus >> 16;
pktlength = rxstatus & 0xffff;
diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c
index bd5916a60cb5..77363d680532 100644
--- a/drivers/net/ethernet/broadcom/b44.c
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -400,7 +400,7 @@ static void b44_set_flow_ctrl(struct b44 *bp, u32 local, u32 remote)
}
#ifdef CONFIG_BCM47XX
-#include <bcm47xx_nvram.h>
+#include <linux/bcm47xx_nvram.h>
static void b44_wap54g10_workaround(struct b44 *bp)
{
char buf[20];
diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c
index 5cb93d1f50a4..de77d3a74abc 100644
--- a/drivers/net/ethernet/broadcom/bgmac.c
+++ b/drivers/net/ethernet/broadcom/bgmac.c
@@ -17,7 +17,7 @@
#include <linux/phy_fixed.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
-#include <bcm47xx_nvram.h>
+#include <linux/bcm47xx_nvram.h>
static const struct bcma_device_id bgmac_bcma_tbl[] = {
BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_4706_MAC_GBIT, BCMA_ANY_REV, BCMA_ANY_CLASS),
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
index 4085c4b31047..355d5fea5be9 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
@@ -531,20 +531,8 @@ struct bnx2x_fastpath {
struct napi_struct napi;
#ifdef CONFIG_NET_RX_BUSY_POLL
- unsigned int state;
-#define BNX2X_FP_STATE_IDLE 0
-#define BNX2X_FP_STATE_NAPI (1 << 0) /* NAPI owns this FP */
-#define BNX2X_FP_STATE_POLL (1 << 1) /* poll owns this FP */
-#define BNX2X_FP_STATE_DISABLED (1 << 2)
-#define BNX2X_FP_STATE_NAPI_YIELD (1 << 3) /* NAPI yielded this FP */
-#define BNX2X_FP_STATE_POLL_YIELD (1 << 4) /* poll yielded this FP */
-#define BNX2X_FP_OWNED (BNX2X_FP_STATE_NAPI | BNX2X_FP_STATE_POLL)
-#define BNX2X_FP_YIELD (BNX2X_FP_STATE_NAPI_YIELD | BNX2X_FP_STATE_POLL_YIELD)
-#define BNX2X_FP_LOCKED (BNX2X_FP_OWNED | BNX2X_FP_STATE_DISABLED)
-#define BNX2X_FP_USER_PEND (BNX2X_FP_STATE_POLL | BNX2X_FP_STATE_POLL_YIELD)
- /* protect state */
- spinlock_t lock;
-#endif /* CONFIG_NET_RX_BUSY_POLL */
+ unsigned long busy_poll_state;
+#endif
union host_hc_status_block status_blk;
/* chip independent shortcuts into sb structure */
@@ -619,104 +607,83 @@ struct bnx2x_fastpath {
#define bnx2x_fp_qstats(bp, fp) (&((bp)->fp_stats[(fp)->index].eth_q_stats))
#ifdef CONFIG_NET_RX_BUSY_POLL
-static inline void bnx2x_fp_init_lock(struct bnx2x_fastpath *fp)
+
+enum bnx2x_fp_state {
+ BNX2X_STATE_FP_NAPI = BIT(0), /* NAPI handler owns the queue */
+
+ BNX2X_STATE_FP_NAPI_REQ_BIT = 1, /* NAPI would like to own the queue */
+ BNX2X_STATE_FP_NAPI_REQ = BIT(1),
+
+ BNX2X_STATE_FP_POLL_BIT = 2,
+ BNX2X_STATE_FP_POLL = BIT(2), /* busy_poll owns the queue */
+
+ BNX2X_STATE_FP_DISABLE_BIT = 3, /* queue is dismantled */
+};
+
+static inline void bnx2x_fp_busy_poll_init(struct bnx2x_fastpath *fp)
{
- spin_lock_init(&fp->lock);
- fp->state = BNX2X_FP_STATE_IDLE;
+ WRITE_ONCE(fp->busy_poll_state, 0);
}
/* called from the device poll routine to get ownership of a FP */
static inline bool bnx2x_fp_lock_napi(struct bnx2x_fastpath *fp)
{
- bool rc = true;
-
- spin_lock_bh(&fp->lock);
- if (fp->state & BNX2X_FP_LOCKED) {
- WARN_ON(fp->state & BNX2X_FP_STATE_NAPI);
- fp->state |= BNX2X_FP_STATE_NAPI_YIELD;
- rc = false;
- } else {
- /* we don't care if someone yielded */
- fp->state = BNX2X_FP_STATE_NAPI;
+ unsigned long prev, old = READ_ONCE(fp->busy_poll_state);
+
+ while (1) {
+ switch (old) {
+ case BNX2X_STATE_FP_POLL:
+ /* make sure bnx2x_fp_lock_poll() wont starve us */
+ set_bit(BNX2X_STATE_FP_NAPI_REQ_BIT,
+ &fp->busy_poll_state);
+ /* fallthrough */
+ case BNX2X_STATE_FP_POLL | BNX2X_STATE_FP_NAPI_REQ:
+ return false;
+ default:
+ break;
+ }
+ prev = cmpxchg(&fp->busy_poll_state, old, BNX2X_STATE_FP_NAPI);
+ if (unlikely(prev != old)) {
+ old = prev;
+ continue;
+ }
+ return true;
}
- spin_unlock_bh(&fp->lock);
- return rc;
}
-/* returns true is someone tried to get the FP while napi had it */
-static inline bool bnx2x_fp_unlock_napi(struct bnx2x_fastpath *fp)
+static inline void bnx2x_fp_unlock_napi(struct bnx2x_fastpath *fp)
{
- bool rc = false;
-
- spin_lock_bh(&fp->lock);
- WARN_ON(fp->state &
- (BNX2X_FP_STATE_POLL | BNX2X_FP_STATE_NAPI_YIELD));
-
- if (fp->state & BNX2X_FP_STATE_POLL_YIELD)
- rc = true;
-
- /* state ==> idle, unless currently disabled */
- fp->state &= BNX2X_FP_STATE_DISABLED;
- spin_unlock_bh(&fp->lock);
- return rc;
+ smp_wmb();
+ fp->busy_poll_state = 0;
}
/* called from bnx2x_low_latency_poll() */
static inline bool bnx2x_fp_lock_poll(struct bnx2x_fastpath *fp)
{
- bool rc = true;
-
- spin_lock_bh(&fp->lock);
- if ((fp->state & BNX2X_FP_LOCKED)) {
- fp->state |= BNX2X_FP_STATE_POLL_YIELD;
- rc = false;
- } else {
- /* preserve yield marks */
- fp->state |= BNX2X_FP_STATE_POLL;
- }
- spin_unlock_bh(&fp->lock);
- return rc;
+ return cmpxchg(&fp->busy_poll_state, 0, BNX2X_STATE_FP_POLL) == 0;
}
-/* returns true if someone tried to get the FP while it was locked */
-static inline bool bnx2x_fp_unlock_poll(struct bnx2x_fastpath *fp)
+static inline void bnx2x_fp_unlock_poll(struct bnx2x_fastpath *fp)
{
- bool rc = false;
-
- spin_lock_bh(&fp->lock);
- WARN_ON(fp->state & BNX2X_FP_STATE_NAPI);
-
- if (fp->state & BNX2X_FP_STATE_POLL_YIELD)
- rc = true;
-
- /* state ==> idle, unless currently disabled */
- fp->state &= BNX2X_FP_STATE_DISABLED;
- spin_unlock_bh(&fp->lock);
- return rc;
+ smp_mb__before_atomic();
+ clear_bit(BNX2X_STATE_FP_POLL_BIT, &fp->busy_poll_state);
}
-/* true if a socket is polling, even if it did not get the lock */
+/* true if a socket is polling */
static inline bool bnx2x_fp_ll_polling(struct bnx2x_fastpath *fp)
{
- WARN_ON(!(fp->state & BNX2X_FP_OWNED));
- return fp->state & BNX2X_FP_USER_PEND;
+ return READ_ONCE(fp->busy_poll_state) & BNX2X_STATE_FP_POLL;
}
/* false if fp is currently owned */
static inline bool bnx2x_fp_ll_disable(struct bnx2x_fastpath *fp)
{
- int rc = true;
-
- spin_lock_bh(&fp->lock);
- if (fp->state & BNX2X_FP_OWNED)
- rc = false;
- fp->state |= BNX2X_FP_STATE_DISABLED;
- spin_unlock_bh(&fp->lock);
+ set_bit(BNX2X_STATE_FP_DISABLE_BIT, &fp->busy_poll_state);
+ return !bnx2x_fp_ll_polling(fp);
- return rc;
}
#else
-static inline void bnx2x_fp_init_lock(struct bnx2x_fastpath *fp)
+static inline void bnx2x_fp_busy_poll_init(struct bnx2x_fastpath *fp)
{
}
@@ -725,9 +692,8 @@ static inline bool bnx2x_fp_lock_napi(struct bnx2x_fastpath *fp)
return true;
}
-static inline bool bnx2x_fp_unlock_napi(struct bnx2x_fastpath *fp)
+static inline void bnx2x_fp_unlock_napi(struct bnx2x_fastpath *fp)
{
- return false;
}
static inline bool bnx2x_fp_lock_poll(struct bnx2x_fastpath *fp)
@@ -735,9 +701,8 @@ static inline bool bnx2x_fp_lock_poll(struct bnx2x_fastpath *fp)
return false;
}
-static inline bool bnx2x_fp_unlock_poll(struct bnx2x_fastpath *fp)
+static inline void bnx2x_fp_unlock_poll(struct bnx2x_fastpath *fp)
{
- return false;
}
static inline bool bnx2x_fp_ll_polling(struct bnx2x_fastpath *fp)
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
index 0a9faa134a9a..2f63467bce46 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
@@ -1849,7 +1849,7 @@ static void bnx2x_napi_enable_cnic(struct bnx2x *bp)
int i;
for_each_rx_queue_cnic(bp, i) {
- bnx2x_fp_init_lock(&bp->fp[i]);
+ bnx2x_fp_busy_poll_init(&bp->fp[i]);
napi_enable(&bnx2x_fp(bp, i, napi));
}
}
@@ -1859,7 +1859,7 @@ static void bnx2x_napi_enable(struct bnx2x *bp)
int i;
for_each_eth_queue(bp, i) {
- bnx2x_fp_init_lock(&bp->fp[i]);
+ bnx2x_fp_busy_poll_init(&bp->fp[i]);
napi_enable(&bnx2x_fp(bp, i, napi));
}
}
@@ -3191,9 +3191,10 @@ static int bnx2x_poll(struct napi_struct *napi, int budget)
}
}
+ bnx2x_fp_unlock_napi(fp);
+
/* Fall out from the NAPI loop if needed */
- if (!bnx2x_fp_unlock_napi(fp) &&
- !(bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp))) {
+ if (!(bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp))) {
/* No need to update SB for FCoE L2 ring as long as
* it's connected to the default SB and the SB
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 6de054404156..803d91beec6f 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -1140,6 +1140,10 @@ static int set_filter_wr(struct adapter *adapter, int fidx)
struct fw_filter_wr *fwr;
unsigned int ftid;
+ skb = alloc_skb(sizeof(*fwr), GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
/* If the new filter requires loopback Destination MAC and/or VLAN
* rewriting then we need to allocate a Layer 2 Table (L2T) entry for
* the filter.
@@ -1147,19 +1151,21 @@ static int set_filter_wr(struct adapter *adapter, int fidx)
if (f->fs.newdmac || f->fs.newvlan) {
/* allocate L2T entry for new filter */
f->l2t = t4_l2t_alloc_switching(adapter->l2t);
- if (f->l2t == NULL)
+ if (f->l2t == NULL) {
+ kfree_skb(skb);
return -EAGAIN;
+ }
if (t4_l2t_set_switching(adapter, f->l2t, f->fs.vlan,
f->fs.eport, f->fs.dmac)) {
cxgb4_l2t_release(f->l2t);
f->l2t = NULL;
+ kfree_skb(skb);
return -ENOMEM;
}
}
ftid = adapter->tids.ftid_base + fidx;
- skb = alloc_skb(sizeof(*fwr), GFP_KERNEL | __GFP_NOFAIL);
fwr = (struct fw_filter_wr *)__skb_put(skb, sizeof(*fwr));
memset(fwr, 0, sizeof(*fwr));
@@ -1257,7 +1263,10 @@ static int del_filter_wr(struct adapter *adapter, int fidx)
len = sizeof(*fwr);
ftid = adapter->tids.ftid_base + fidx;
- skb = alloc_skb(len, GFP_KERNEL | __GFP_NOFAIL);
+ skb = alloc_skb(len, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
fwr = (struct fw_filter_wr *)__skb_put(skb, len);
t4_mk_filtdelwr(ftid, fwr, adapter->sge.fw_evtq.abs_id);
diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c
index b72d238695d7..3b39fdddeb57 100644
--- a/drivers/net/ethernet/hisilicon/hip04_eth.c
+++ b/drivers/net/ethernet/hisilicon/hip04_eth.c
@@ -413,6 +413,15 @@ out:
return count;
}
+static void hip04_start_tx_timer(struct hip04_priv *priv)
+{
+ unsigned long ns = priv->tx_coalesce_usecs * NSEC_PER_USEC / 2;
+
+ /* allow timer to fire after half the time at the earliest */
+ hrtimer_start_range_ns(&priv->tx_coalesce_timer, ns_to_ktime(ns),
+ ns, HRTIMER_MODE_REL);
+}
+
static int hip04_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct hip04_priv *priv = netdev_priv(ndev);
@@ -466,8 +475,7 @@ static int hip04_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
}
} else if (!hrtimer_is_queued(&priv->tx_coalesce_timer)) {
/* cleanup not pending yet, start a new timer */
- hrtimer_start_expires(&priv->tx_coalesce_timer,
- HRTIMER_MODE_REL);
+ hip04_start_tx_timer(priv);
}
return NETDEV_TX_OK;
@@ -549,7 +557,7 @@ done:
/* clean up tx descriptors and start a new timer if necessary */
tx_remaining = hip04_tx_reclaim(ndev, false);
if (rx < budget && tx_remaining)
- hrtimer_start_expires(&priv->tx_coalesce_timer, HRTIMER_MODE_REL);
+ hip04_start_tx_timer(priv);
return rx;
}
@@ -809,7 +817,6 @@ static int hip04_mac_probe(struct platform_device *pdev)
struct hip04_priv *priv;
struct resource *res;
unsigned int irq;
- ktime_t txtime;
int ret;
ndev = alloc_etherdev(sizeof(struct hip04_priv));
@@ -846,9 +853,6 @@ static int hip04_mac_probe(struct platform_device *pdev)
*/
priv->tx_coalesce_frames = TX_DESC_NUM * 3 / 4;
priv->tx_coalesce_usecs = 200;
- /* allow timer to fire after half the time at the earliest */
- txtime = ktime_set(0, priv->tx_coalesce_usecs * NSEC_PER_USEC / 2);
- hrtimer_set_expires_range(&priv->tx_coalesce_timer, txtime, txtime);
priv->tx_coalesce_timer.function = tx_done;
priv->map = syscon_node_to_regmap(arg.np);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
index d596f6624025..0bae22da014d 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
@@ -2397,6 +2397,7 @@ i40e_aq_erase_nvm_exit:
#define I40E_DEV_FUNC_CAP_LED 0x61
#define I40E_DEV_FUNC_CAP_SDP 0x62
#define I40E_DEV_FUNC_CAP_MDIO 0x63
+#define I40E_DEV_FUNC_CAP_WR_CSR_PROT 0x64
/**
* i40e_parse_discover_capabilities
@@ -2541,11 +2542,18 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff,
p->fd_filters_guaranteed = number;
p->fd_filters_best_effort = logical_id;
break;
+ case I40E_DEV_FUNC_CAP_WR_CSR_PROT:
+ p->wr_csr_prot = (u64)number;
+ p->wr_csr_prot |= (u64)logical_id << 32;
+ break;
default:
break;
}
}
+ if (p->fcoe)
+ i40e_debug(hw, I40E_DEBUG_ALL, "device is FCoE capable\n");
+
/* Software override ensuring FCoE is disabled if npar or mfp
* mode because it is not supported in these modes.
*/
@@ -3503,6 +3511,63 @@ void i40e_set_pci_config_data(struct i40e_hw *hw, u16 link_status)
}
/**
+ * i40e_aq_debug_dump
+ * @hw: pointer to the hardware structure
+ * @cluster_id: specific cluster to dump
+ * @table_id: table id within cluster
+ * @start_index: index of line in the block to read
+ * @buff_size: dump buffer size
+ * @buff: dump buffer
+ * @ret_buff_size: actual buffer size returned
+ * @ret_next_table: next block to read
+ * @ret_next_index: next index to read
+ *
+ * Dump internal FW/HW data for debug purposes.
+ *
+ **/
+i40e_status i40e_aq_debug_dump(struct i40e_hw *hw, u8 cluster_id,
+ u8 table_id, u32 start_index, u16 buff_size,
+ void *buff, u16 *ret_buff_size,
+ u8 *ret_next_table, u32 *ret_next_index,
+ struct i40e_asq_cmd_details *cmd_details)
+{
+ struct i40e_aq_desc desc;
+ struct i40e_aqc_debug_dump_internals *cmd =
+ (struct i40e_aqc_debug_dump_internals *)&desc.params.raw;
+ struct i40e_aqc_debug_dump_internals *resp =
+ (struct i40e_aqc_debug_dump_internals *)&desc.params.raw;
+ i40e_status status;
+
+ if (buff_size == 0 || !buff)
+ return I40E_ERR_PARAM;
+
+ i40e_fill_default_direct_cmd_desc(&desc,
+ i40e_aqc_opc_debug_dump_internals);
+ /* Indirect Command */
+ desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF);
+ if (buff_size > I40E_AQ_LARGE_BUF)
+ desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB);
+
+ cmd->cluster_id = cluster_id;
+ cmd->table_id = table_id;
+ cmd->idx = cpu_to_le32(start_index);
+
+ desc.datalen = cpu_to_le16(buff_size);
+
+ status = i40e_asq_send_command(hw, &desc, buff, buff_size, cmd_details);
+ if (!status) {
+ if (ret_buff_size)
+ *ret_buff_size = le16_to_cpu(desc.datalen);
+ if (ret_next_table)
+ *ret_next_table = resp->table_id;
+ if (ret_next_index)
+ *ret_next_index = le32_to_cpu(resp->idx);
+ }
+
+ return status;
+}
+
+/**
* i40e_read_bw_from_alt_ram
* @hw: pointer to the hardware structure
* @max_bw: pointer for max_bw read
diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.c b/drivers/net/ethernet/intel/i40e/i40e_dcb.c
index 6e1466756760..2547aa21b2ca 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_dcb.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.c
@@ -419,7 +419,7 @@ static void i40e_cee_to_dcb_v1_config(
{
u16 status, tlv_status = le16_to_cpu(cee_cfg->tlv_status);
u16 app_prio = le16_to_cpu(cee_cfg->oper_app_prio);
- u8 i, tc, err, sync, oper;
+ u8 i, tc, err;
/* CEE PG data to ETS config */
dcbcfg->etscfg.maxtcs = cee_cfg->oper_num_tc;
@@ -456,9 +456,7 @@ static void i40e_cee_to_dcb_v1_config(
status = (tlv_status & I40E_AQC_CEE_APP_STATUS_MASK) >>
I40E_AQC_CEE_APP_STATUS_SHIFT;
err = (status & I40E_TLV_STATUS_ERR) ? 1 : 0;
- sync = (status & I40E_TLV_STATUS_SYNC) ? 1 : 0;
- oper = (status & I40E_TLV_STATUS_OPER) ? 1 : 0;
- /* Add APPs if Error is False and Oper/Sync is True */
+ /* Add APPs if Error is False */
if (!err) {
/* CEE operating configuration supports FCoE/iSCSI/FIP only */
dcbcfg->numapps = I40E_CEE_OPER_MAX_APPS;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
index daa88263af66..34170eabca7d 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
@@ -1388,6 +1388,50 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
r_cfg->app[i].selector,
r_cfg->app[i].protocolid);
}
+ } else if (strncmp(&cmd_buf[5], "debug fwdata", 12) == 0) {
+ int cluster_id, table_id;
+ int index, ret;
+ u16 buff_len = 4096;
+ u32 next_index;
+ u8 next_table;
+ u8 *buff;
+ u16 rlen;
+
+ cnt = sscanf(&cmd_buf[18], "%i %i %i",
+ &cluster_id, &table_id, &index);
+ if (cnt != 3) {
+ dev_info(&pf->pdev->dev,
+ "dump debug fwdata <cluster_id> <table_id> <index>\n");
+ goto command_write_done;
+ }
+
+ dev_info(&pf->pdev->dev,
+ "AQ debug dump fwdata params %x %x %x %x\n",
+ cluster_id, table_id, index, buff_len);
+ buff = kzalloc(buff_len, GFP_KERNEL);
+ if (!buff)
+ goto command_write_done;
+
+ ret = i40e_aq_debug_dump(&pf->hw, cluster_id, table_id,
+ index, buff_len, buff, &rlen,
+ &next_table, &next_index,
+ NULL);
+ if (ret) {
+ dev_info(&pf->pdev->dev,
+ "debug dump fwdata AQ Failed %d 0x%x\n",
+ ret, pf->hw.aq.asq_last_status);
+ kfree(buff);
+ buff = NULL;
+ goto command_write_done;
+ }
+ dev_info(&pf->pdev->dev,
+ "AQ debug dump fwdata rlen=0x%x next_table=0x%x next_index=0x%x\n",
+ rlen, next_table, next_index);
+ print_hex_dump(KERN_INFO, "AQ buffer WB: ",
+ DUMP_PREFIX_OFFSET, 16, 1,
+ buff, rlen, true);
+ kfree(buff);
+ buff = NULL;
} else {
dev_info(&pf->pdev->dev,
"dump desc tx <vsi_seid> <ring_id> [<desc_n>], dump desc rx <vsi_seid> <ring_id> [<desc_n>],\n");
@@ -1903,6 +1947,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
dev_info(&pf->pdev->dev, " dump desc rx <vsi_seid> <ring_id> [<desc_n>]\n");
dev_info(&pf->pdev->dev, " dump desc aq\n");
dev_info(&pf->pdev->dev, " dump reset stats\n");
+ dev_info(&pf->pdev->dev, " dump debug fwdata <cluster_id> <table_id> <index>\n");
dev_info(&pf->pdev->dev, " msg_enable [level]\n");
dev_info(&pf->pdev->dev, " read <reg>\n");
dev_info(&pf->pdev->dev, " write <reg> <value>\n");
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
index c848b1862512..4cbaaeb902c4 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
@@ -356,8 +356,7 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw,
/* Set speed and duplex */
switch (link_speed) {
case I40E_LINK_SPEED_40GB:
- /* need a SPEED_40000 in ethtool.h */
- ethtool_cmd_speed_set(ecmd, 40000);
+ ethtool_cmd_speed_set(ecmd, SPEED_40000);
break;
case I40E_LINK_SPEED_20GB:
ethtool_cmd_speed_set(ecmd, SPEED_20000);
@@ -1914,6 +1913,16 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf,
else
fsp->ring_cookie = rule->q_index;
+ if (rule->dest_vsi != pf->vsi[pf->lan_vsi]->id) {
+ struct i40e_vsi *vsi;
+
+ vsi = i40e_find_vsi_from_id(pf, rule->dest_vsi);
+ if (vsi && vsi->type == I40E_VSI_SRIOV) {
+ fsp->h_ext.data[1] = htonl(vsi->vf_id);
+ fsp->m_ext.data[1] = htonl(0x1);
+ }
+ }
+
return 0;
}
@@ -2207,6 +2216,7 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi,
struct i40e_fdir_filter *input;
struct i40e_pf *pf;
int ret = -EINVAL;
+ u16 vf_id;
if (!vsi)
return -EINVAL;
@@ -2267,7 +2277,22 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi,
input->dst_ip[0] = fsp->h_u.tcp_ip4_spec.ip4src;
input->src_ip[0] = fsp->h_u.tcp_ip4_spec.ip4dst;
+ if (ntohl(fsp->m_ext.data[1])) {
+ if (ntohl(fsp->h_ext.data[1]) >= pf->num_alloc_vfs) {
+ netif_info(pf, drv, vsi->netdev, "Invalid VF id\n");
+ goto free_input;
+ }
+ vf_id = ntohl(fsp->h_ext.data[1]);
+ /* Find vsi id from vf id and override dest vsi */
+ input->dest_vsi = pf->vf[vf_id].lan_vsi_id;
+ if (input->q_index >= pf->vf[vf_id].num_queue_pairs) {
+ netif_info(pf, drv, vsi->netdev, "Invalid queue id\n");
+ goto free_input;
+ }
+ }
+
ret = i40e_add_del_fdir(vsi, input, true);
+free_input:
if (ret)
kfree(input);
else
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 63de3f4b7a94..24481cd7e59a 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -39,7 +39,7 @@ static const char i40e_driver_string[] =
#define DRV_VERSION_MAJOR 1
#define DRV_VERSION_MINOR 3
-#define DRV_VERSION_BUILD 1
+#define DRV_VERSION_BUILD 2
#define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \
__stringify(DRV_VERSION_MINOR) "." \
__stringify(DRV_VERSION_BUILD) DRV_KERN
@@ -7301,7 +7301,7 @@ err_out:
* i40e_init_interrupt_scheme - Determine proper interrupt scheme
* @pf: board private structure to initialize
**/
-static void i40e_init_interrupt_scheme(struct i40e_pf *pf)
+static int i40e_init_interrupt_scheme(struct i40e_pf *pf)
{
int vectors = 0;
ssize_t size;
@@ -7343,11 +7343,17 @@ static void i40e_init_interrupt_scheme(struct i40e_pf *pf)
/* set up vector assignment tracking */
size = sizeof(struct i40e_lump_tracking) + (sizeof(u16) * vectors);
pf->irq_pile = kzalloc(size, GFP_KERNEL);
+ if (!pf->irq_pile) {
+ dev_err(&pf->pdev->dev, "error allocating irq_pile memory\n");
+ return -ENOMEM;
+ }
pf->irq_pile->num_entries = vectors;
pf->irq_pile->search_hint = 0;
- /* track first vector for misc interrupts */
+ /* track first vector for misc interrupts, ignore return */
(void)i40e_get_lump(pf, pf->irq_pile, 1, I40E_PILE_VALID_BIT - 1);
+
+ return 0;
}
/**
@@ -9827,7 +9833,9 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* set up the main switch operations */
i40e_determine_queue_usage(pf);
- i40e_init_interrupt_scheme(pf);
+ err = i40e_init_interrupt_scheme(pf);
+ if (err)
+ goto err_switch_setup;
/* The number of VSIs reported by the FW is the minimum guaranteed
* to us; HW supports far more and we share the remaining pool with
diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c
index e49acd2accd3..554e49d02683 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c
@@ -821,13 +821,12 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw,
int *errno)
{
enum i40e_nvmupd_cmd upd_cmd;
- u8 transaction, module;
+ u8 transaction;
/* anything that doesn't match a recognized case is an error */
upd_cmd = I40E_NVMUPD_INVALID;
transaction = i40e_nvmupd_get_transaction(cmd->config);
- module = i40e_nvmupd_get_module(cmd->config);
/* limits on data size */
if ((cmd->data_size < 1) ||
diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
index fea0d37ecc72..7b34f1e660ea 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
@@ -303,4 +303,9 @@ i40e_status i40e_aq_add_rem_control_packet_filter(struct i40e_hw *hw,
u16 vsi_seid, u16 queue, bool is_add,
struct i40e_control_filter_stats *stats,
struct i40e_asq_cmd_details *cmd_details);
+i40e_status i40e_aq_debug_dump(struct i40e_hw *hw, u8 cluster_id,
+ u8 table_id, u32 start_index, u16 buff_size,
+ void *buff, u16 *ret_buff_size,
+ u8 *ret_next_table, u32 *ret_next_index,
+ struct i40e_asq_cmd_details *cmd_details);
#endif /* _I40E_PROTOTYPE_H_ */
diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h
index 67c7bc9e9c21..568e855da0f3 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_type.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_type.h
@@ -242,6 +242,7 @@ struct i40e_hw_capabilities {
u8 rx_buf_chain_len;
u32 enabled_tcmap;
u32 maxtc;
+ u64 wr_csr_prot;
};
struct i40e_mac_info {
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
index 4d69e1f04901..78d1c4ff565e 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
@@ -26,6 +26,129 @@
#include "i40e.h"
+/*********************notification routines***********************/
+
+/**
+ * i40e_vc_vf_broadcast
+ * @pf: pointer to the PF structure
+ * @opcode: operation code
+ * @retval: return value
+ * @msg: pointer to the msg buffer
+ * @msglen: msg length
+ *
+ * send a message to all VFs on a given PF
+ **/
+static void i40e_vc_vf_broadcast(struct i40e_pf *pf,
+ enum i40e_virtchnl_ops v_opcode,
+ i40e_status v_retval, u8 *msg,
+ u16 msglen)
+{
+ struct i40e_hw *hw = &pf->hw;
+ struct i40e_vf *vf = pf->vf;
+ int i;
+
+ for (i = 0; i < pf->num_alloc_vfs; i++, vf++) {
+ int abs_vf_id = vf->vf_id + hw->func_caps.vf_base_id;
+ /* Not all vfs are enabled so skip the ones that are not */
+ if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states) &&
+ !test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states))
+ continue;
+
+ /* Ignore return value on purpose - a given VF may fail, but
+ * we need to keep going and send to all of them
+ */
+ i40e_aq_send_msg_to_vf(hw, abs_vf_id, v_opcode, v_retval,
+ msg, msglen, NULL);
+ }
+}
+
+/**
+ * i40e_vc_notify_link_state
+ * @vf: pointer to the VF structure
+ *
+ * send a link status message to a single VF
+ **/
+static void i40e_vc_notify_vf_link_state(struct i40e_vf *vf)
+{
+ struct i40e_virtchnl_pf_event pfe;
+ struct i40e_pf *pf = vf->pf;
+ struct i40e_hw *hw = &pf->hw;
+ struct i40e_link_status *ls = &pf->hw.phy.link_info;
+ int abs_vf_id = vf->vf_id + hw->func_caps.vf_base_id;
+
+ pfe.event = I40E_VIRTCHNL_EVENT_LINK_CHANGE;
+ pfe.severity = I40E_PF_EVENT_SEVERITY_INFO;
+ if (vf->link_forced) {
+ pfe.event_data.link_event.link_status = vf->link_up;
+ pfe.event_data.link_event.link_speed =
+ (vf->link_up ? I40E_LINK_SPEED_40GB : 0);
+ } else {
+ pfe.event_data.link_event.link_status =
+ ls->link_info & I40E_AQ_LINK_UP;
+ pfe.event_data.link_event.link_speed = ls->link_speed;
+ }
+ i40e_aq_send_msg_to_vf(hw, abs_vf_id, I40E_VIRTCHNL_OP_EVENT,
+ 0, (u8 *)&pfe, sizeof(pfe), NULL);
+}
+
+/**
+ * i40e_vc_notify_link_state
+ * @pf: pointer to the PF structure
+ *
+ * send a link status message to all VFs on a given PF
+ **/
+void i40e_vc_notify_link_state(struct i40e_pf *pf)
+{
+ int i;
+
+ for (i = 0; i < pf->num_alloc_vfs; i++)
+ i40e_vc_notify_vf_link_state(&pf->vf[i]);
+}
+
+/**
+ * i40e_vc_notify_reset
+ * @pf: pointer to the PF structure
+ *
+ * indicate a pending reset to all VFs on a given PF
+ **/
+void i40e_vc_notify_reset(struct i40e_pf *pf)
+{
+ struct i40e_virtchnl_pf_event pfe;
+
+ pfe.event = I40E_VIRTCHNL_EVENT_RESET_IMPENDING;
+ pfe.severity = I40E_PF_EVENT_SEVERITY_CERTAIN_DOOM;
+ i40e_vc_vf_broadcast(pf, I40E_VIRTCHNL_OP_EVENT, 0,
+ (u8 *)&pfe, sizeof(struct i40e_virtchnl_pf_event));
+}
+
+/**
+ * i40e_vc_notify_vf_reset
+ * @vf: pointer to the VF structure
+ *
+ * indicate a pending reset to the given VF
+ **/
+void i40e_vc_notify_vf_reset(struct i40e_vf *vf)
+{
+ struct i40e_virtchnl_pf_event pfe;
+ int abs_vf_id;
+
+ /* validate the request */
+ if (!vf || vf->vf_id >= vf->pf->num_alloc_vfs)
+ return;
+
+ /* verify if the VF is in either init or active before proceeding */
+ if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states) &&
+ !test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states))
+ return;
+
+ abs_vf_id = vf->vf_id + vf->pf->hw.func_caps.vf_base_id;
+
+ pfe.event = I40E_VIRTCHNL_EVENT_RESET_IMPENDING;
+ pfe.severity = I40E_PF_EVENT_SEVERITY_CERTAIN_DOOM;
+ i40e_aq_send_msg_to_vf(&vf->pf->hw, abs_vf_id, I40E_VIRTCHNL_OP_EVENT,
+ 0, (u8 *)&pfe,
+ sizeof(struct i40e_virtchnl_pf_event), NULL);
+}
/***********************misc routines*****************************/
/**
@@ -689,6 +812,9 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr)
}
}
+ if (flr)
+ usleep_range(10000, 20000);
+
if (!rsd)
dev_err(&pf->pdev->dev, "VF reset check timeout on VF %d\n",
vf->vf_id);
@@ -733,6 +859,11 @@ void i40e_free_vfs(struct i40e_pf *pf)
while (test_and_set_bit(__I40E_VF_DISABLE, &pf->state))
usleep_range(1000, 2000);
+ for (i = 0; i < pf->num_alloc_vfs; i++)
+ if (test_bit(I40E_VF_STAT_INIT, &pf->vf[i].vf_states))
+ i40e_vsi_control_rings(pf->vsi[pf->vf[i].lan_vsi_idx],
+ false);
+
/* Disable IOV before freeing resources. This lets any VF drivers
* running in the host get themselves cleaned up before we yank
* the carpet out from underneath their feet.
@@ -1762,6 +1893,7 @@ int i40e_vc_process_vf_msg(struct i40e_pf *pf, u16 vf_id, u32 v_opcode,
break;
case I40E_VIRTCHNL_OP_ENABLE_QUEUES:
ret = i40e_vc_enable_queues_msg(vf, msg, msglen);
+ i40e_vc_notify_vf_link_state(vf);
break;
case I40E_VIRTCHNL_OP_DISABLE_QUEUES:
ret = i40e_vc_disable_queues_msg(vf, msg, msglen);
@@ -1835,118 +1967,6 @@ int i40e_vc_process_vflr_event(struct i40e_pf *pf)
}
/**
- * i40e_vc_vf_broadcast
- * @pf: pointer to the PF structure
- * @opcode: operation code
- * @retval: return value
- * @msg: pointer to the msg buffer
- * @msglen: msg length
- *
- * send a message to all VFs on a given PF
- **/
-static void i40e_vc_vf_broadcast(struct i40e_pf *pf,
- enum i40e_virtchnl_ops v_opcode,
- i40e_status v_retval, u8 *msg,
- u16 msglen)
-{
- struct i40e_hw *hw = &pf->hw;
- struct i40e_vf *vf = pf->vf;
- int i;
-
- for (i = 0; i < pf->num_alloc_vfs; i++, vf++) {
- int abs_vf_id = vf->vf_id + hw->func_caps.vf_base_id;
- /* Not all VFs are enabled so skip the ones that are not */
- if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states) &&
- !test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states))
- continue;
-
- /* Ignore return value on purpose - a given VF may fail, but
- * we need to keep going and send to all of them
- */
- i40e_aq_send_msg_to_vf(hw, abs_vf_id, v_opcode, v_retval,
- msg, msglen, NULL);
- }
-}
-
-/**
- * i40e_vc_notify_link_state
- * @pf: pointer to the PF structure
- *
- * send a link status message to all VFs on a given PF
- **/
-void i40e_vc_notify_link_state(struct i40e_pf *pf)
-{
- struct i40e_virtchnl_pf_event pfe;
- struct i40e_hw *hw = &pf->hw;
- struct i40e_vf *vf = pf->vf;
- struct i40e_link_status *ls = &pf->hw.phy.link_info;
- int i;
-
- pfe.event = I40E_VIRTCHNL_EVENT_LINK_CHANGE;
- pfe.severity = I40E_PF_EVENT_SEVERITY_INFO;
- for (i = 0; i < pf->num_alloc_vfs; i++, vf++) {
- int abs_vf_id = vf->vf_id + hw->func_caps.vf_base_id;
- if (vf->link_forced) {
- pfe.event_data.link_event.link_status = vf->link_up;
- pfe.event_data.link_event.link_speed =
- (vf->link_up ? I40E_LINK_SPEED_40GB : 0);
- } else {
- pfe.event_data.link_event.link_status =
- ls->link_info & I40E_AQ_LINK_UP;
- pfe.event_data.link_event.link_speed = ls->link_speed;
- }
- i40e_aq_send_msg_to_vf(hw, abs_vf_id, I40E_VIRTCHNL_OP_EVENT,
- 0, (u8 *)&pfe, sizeof(pfe),
- NULL);
- }
-}
-
-/**
- * i40e_vc_notify_reset
- * @pf: pointer to the PF structure
- *
- * indicate a pending reset to all VFs on a given PF
- **/
-void i40e_vc_notify_reset(struct i40e_pf *pf)
-{
- struct i40e_virtchnl_pf_event pfe;
-
- pfe.event = I40E_VIRTCHNL_EVENT_RESET_IMPENDING;
- pfe.severity = I40E_PF_EVENT_SEVERITY_CERTAIN_DOOM;
- i40e_vc_vf_broadcast(pf, I40E_VIRTCHNL_OP_EVENT, I40E_SUCCESS,
- (u8 *)&pfe, sizeof(struct i40e_virtchnl_pf_event));
-}
-
-/**
- * i40e_vc_notify_vf_reset
- * @vf: pointer to the VF structure
- *
- * indicate a pending reset to the given VF
- **/
-void i40e_vc_notify_vf_reset(struct i40e_vf *vf)
-{
- struct i40e_virtchnl_pf_event pfe;
- int abs_vf_id;
-
- /* validate the request */
- if (!vf || vf->vf_id >= vf->pf->num_alloc_vfs)
- return;
-
- /* verify if the VF is in either init or active before proceeding */
- if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states) &&
- !test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states))
- return;
-
- abs_vf_id = vf->vf_id + vf->pf->hw.func_caps.vf_base_id;
-
- pfe.event = I40E_VIRTCHNL_EVENT_RESET_IMPENDING;
- pfe.severity = I40E_PF_EVENT_SEVERITY_CERTAIN_DOOM;
- i40e_aq_send_msg_to_vf(&vf->pf->hw, abs_vf_id, I40E_VIRTCHNL_OP_EVENT,
- I40E_SUCCESS, (u8 *)&pfe,
- sizeof(struct i40e_virtchnl_pf_event), NULL);
-}
-
-/**
* i40e_ndo_set_vf_mac
* @netdev: network interface device structure
* @vf_id: VF identifier
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h
index 9c79cb6abb2b..ec9d83a93379 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_type.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h
@@ -242,6 +242,7 @@ struct i40e_hw_capabilities {
u8 rx_buf_chain_len;
u32 enabled_tcmap;
u32 maxtc;
+ u64 wr_csr_prot;
};
struct i40e_mac_info {
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h
index 34c8565031f6..1b98c25b3092 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf.h
+++ b/drivers/net/ethernet/intel/i40evf/i40evf.h
@@ -225,7 +225,6 @@ struct i40evf_adapter {
#define I40E_FLAG_RX_CSUM_ENABLED I40EVF_FLAG_RX_CSUM_ENABLED
/* flags for admin queue service task */
u32 aq_required;
- u32 aq_pending;
#define I40EVF_FLAG_AQ_ENABLE_QUEUES (u32)(1)
#define I40EVF_FLAG_AQ_DISABLE_QUEUES (u32)(1 << 1)
#define I40EVF_FLAG_AQ_ADD_MAC_FILTER (u32)(1 << 2)
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
index 6d5f3b21c68a..7c53aca4b5a6 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
@@ -1008,7 +1008,6 @@ void i40evf_down(struct i40evf_adapter *adapter)
adapter->state != __I40EVF_RESETTING) {
/* cancel any current operation */
adapter->current_op = I40E_VIRTCHNL_OP_UNKNOWN;
- adapter->aq_pending = 0;
/* Schedule operations to close down the HW. Don't wait
* here for this to complete. The watchdog is still running
* and it will take care of this.
@@ -1335,7 +1334,6 @@ static void i40evf_watchdog_task(struct work_struct *work)
*/
return;
}
- adapter->aq_pending = 0;
adapter->aq_required = 0;
adapter->current_op = I40E_VIRTCHNL_OP_UNKNOWN;
goto watchdog_done;
@@ -1355,7 +1353,6 @@ static void i40evf_watchdog_task(struct work_struct *work)
adapter->flags |= I40EVF_FLAG_RESET_PENDING;
dev_err(&adapter->pdev->dev, "Hardware reset detected\n");
schedule_work(&adapter->reset_task);
- adapter->aq_pending = 0;
adapter->aq_required = 0;
adapter->current_op = I40E_VIRTCHNL_OP_UNKNOWN;
goto watchdog_done;
@@ -1364,7 +1361,7 @@ static void i40evf_watchdog_task(struct work_struct *work)
/* Process admin queue tasks. After init, everything gets done
* here so we don't race on the admin queue.
*/
- if (adapter->aq_pending) {
+ if (adapter->current_op) {
if (!i40evf_asq_done(hw)) {
dev_dbg(&adapter->pdev->dev, "Admin queue timeout\n");
i40evf_send_api_ver(adapter);
@@ -2029,7 +2026,7 @@ static void i40evf_init_task(struct work_struct *work)
if (err) {
dev_err(&pdev->dev, "Failed to set MAC type (%d)\n",
err);
- goto err;
+ goto err;
}
err = i40evf_check_reset_complete(hw);
if (err) {
@@ -2249,7 +2246,6 @@ static void i40evf_shutdown(struct pci_dev *pdev)
/* Prevent the watchdog from running. */
adapter->state = __I40EVF_REMOVE;
adapter->aq_required = 0;
- adapter->aq_pending = 0;
#ifdef CONFIG_PM
pci_save_state(pdev);
@@ -2467,7 +2463,6 @@ static void i40evf_remove(struct pci_dev *pdev)
/* Shut down all the garbage mashers on the detention level */
adapter->state = __I40EVF_REMOVE;
adapter->aq_required = 0;
- adapter->aq_pending = 0;
i40evf_request_reset(adapter);
msleep(20);
/* If the FW isn't responding, kick it once, but only once. */
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
index 4240a496dc50..61e090558f31 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
@@ -250,7 +250,6 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter)
vqpi++;
}
- adapter->aq_pending |= I40EVF_FLAG_AQ_CONFIGURE_QUEUES;
adapter->aq_required &= ~I40EVF_FLAG_AQ_CONFIGURE_QUEUES;
i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_CONFIG_VSI_QUEUES,
(u8 *)vqci, len);
@@ -277,7 +276,6 @@ void i40evf_enable_queues(struct i40evf_adapter *adapter)
vqs.vsi_id = adapter->vsi_res->vsi_id;
vqs.tx_queues = (1 << adapter->num_active_queues) - 1;
vqs.rx_queues = vqs.tx_queues;
- adapter->aq_pending |= I40EVF_FLAG_AQ_ENABLE_QUEUES;
adapter->aq_required &= ~I40EVF_FLAG_AQ_ENABLE_QUEUES;
i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_ENABLE_QUEUES,
(u8 *)&vqs, sizeof(vqs));
@@ -303,7 +301,6 @@ void i40evf_disable_queues(struct i40evf_adapter *adapter)
vqs.vsi_id = adapter->vsi_res->vsi_id;
vqs.tx_queues = (1 << adapter->num_active_queues) - 1;
vqs.rx_queues = vqs.tx_queues;
- adapter->aq_pending |= I40EVF_FLAG_AQ_DISABLE_QUEUES;
adapter->aq_required &= ~I40EVF_FLAG_AQ_DISABLE_QUEUES;
i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_DISABLE_QUEUES,
(u8 *)&vqs, sizeof(vqs));
@@ -354,7 +351,6 @@ void i40evf_map_queues(struct i40evf_adapter *adapter)
vimi->vecmap[v_idx].txq_map = 0;
vimi->vecmap[v_idx].rxq_map = 0;
- adapter->aq_pending |= I40EVF_FLAG_AQ_MAP_VECTORS;
adapter->aq_required &= ~I40EVF_FLAG_AQ_MAP_VECTORS;
i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_CONFIG_IRQ_MAP,
(u8 *)vimi, len);
@@ -415,7 +411,6 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter)
f->add = false;
}
}
- adapter->aq_pending |= I40EVF_FLAG_AQ_ADD_MAC_FILTER;
adapter->aq_required &= ~I40EVF_FLAG_AQ_ADD_MAC_FILTER;
i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_ADD_ETHER_ADDRESS,
(u8 *)veal, len);
@@ -476,7 +471,6 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter)
kfree(f);
}
}
- adapter->aq_pending |= I40EVF_FLAG_AQ_DEL_MAC_FILTER;
adapter->aq_required &= ~I40EVF_FLAG_AQ_DEL_MAC_FILTER;
i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_DEL_ETHER_ADDRESS,
(u8 *)veal, len);
@@ -537,7 +531,6 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter)
f->add = false;
}
}
- adapter->aq_pending |= I40EVF_FLAG_AQ_ADD_VLAN_FILTER;
adapter->aq_required &= ~I40EVF_FLAG_AQ_ADD_VLAN_FILTER;
i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_ADD_VLAN, (u8 *)vvfl, len);
kfree(vvfl);
@@ -598,7 +591,6 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter)
kfree(f);
}
}
- adapter->aq_pending |= I40EVF_FLAG_AQ_DEL_VLAN_FILTER;
adapter->aq_required &= ~I40EVF_FLAG_AQ_DEL_VLAN_FILTER;
i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_DEL_VLAN, (u8 *)vvfl, len);
kfree(vvfl);
@@ -720,9 +712,6 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
__func__, v_retval, v_opcode);
}
switch (v_opcode) {
- case I40E_VIRTCHNL_OP_VERSION:
- /* no action, but also not an error */
- break;
case I40E_VIRTCHNL_OP_GET_STATS: {
struct i40e_eth_stats *stats =
(struct i40e_eth_stats *)msg;
@@ -740,39 +729,30 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
adapter->current_stats = *stats;
}
break;
- case I40E_VIRTCHNL_OP_ADD_ETHER_ADDRESS:
- adapter->aq_pending &= ~(I40EVF_FLAG_AQ_ADD_MAC_FILTER);
- break;
- case I40E_VIRTCHNL_OP_DEL_ETHER_ADDRESS:
- adapter->aq_pending &= ~(I40EVF_FLAG_AQ_DEL_MAC_FILTER);
- break;
- case I40E_VIRTCHNL_OP_ADD_VLAN:
- adapter->aq_pending &= ~(I40EVF_FLAG_AQ_ADD_VLAN_FILTER);
- break;
- case I40E_VIRTCHNL_OP_DEL_VLAN:
- adapter->aq_pending &= ~(I40EVF_FLAG_AQ_DEL_VLAN_FILTER);
- break;
case I40E_VIRTCHNL_OP_ENABLE_QUEUES:
- adapter->aq_pending &= ~(I40EVF_FLAG_AQ_ENABLE_QUEUES);
/* enable transmits */
i40evf_irq_enable(adapter, true);
netif_tx_start_all_queues(adapter->netdev);
netif_carrier_on(adapter->netdev);
break;
case I40E_VIRTCHNL_OP_DISABLE_QUEUES:
- adapter->aq_pending &= ~(I40EVF_FLAG_AQ_DISABLE_QUEUES);
i40evf_free_all_tx_resources(adapter);
i40evf_free_all_rx_resources(adapter);
break;
- case I40E_VIRTCHNL_OP_CONFIG_VSI_QUEUES:
- adapter->aq_pending &= ~(I40EVF_FLAG_AQ_CONFIGURE_QUEUES);
- break;
+ case I40E_VIRTCHNL_OP_VERSION:
+ case I40E_VIRTCHNL_OP_GET_VF_RESOURCES:
case I40E_VIRTCHNL_OP_CONFIG_IRQ_MAP:
- adapter->aq_pending &= ~(I40EVF_FLAG_AQ_MAP_VECTORS);
+ /* Don't display an error if we get these out of sequence.
+ * If the firmware needed to get kicked, we'll get these and
+ * it's no problem.
+ */
+ if (v_opcode != adapter->current_op)
+ return;
break;
default:
- dev_info(&adapter->pdev->dev, "Received unexpected message %d from PF\n",
- v_opcode);
+ if (v_opcode != adapter->current_op)
+ dev_warn(&adapter->pdev->dev, "Expected response %d from PF, received %d\n",
+ adapter->current_op, v_opcode);
break;
} /* switch v_opcode */
adapter->current_op = I40E_VIRTCHNL_OP_UNKNOWN;
diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c
index 4a42e960d331..f66641d961e3 100644
--- a/drivers/net/ethernet/qualcomm/qca_spi.c
+++ b/drivers/net/ethernet/qualcomm/qca_spi.c
@@ -41,7 +41,6 @@
#include <linux/skbuff.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
-#include <linux/version.h>
#include "qca_7k.h"
#include "qca_debug.h"
diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c
index a87b177bd723..a570a60533be 100644
--- a/drivers/net/ethernet/rocker/rocker.c
+++ b/drivers/net/ethernet/rocker/rocker.c
@@ -4759,6 +4759,7 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (pci_resource_len(pdev, 0) < ROCKER_PCI_BAR0_SIZE) {
dev_err(&pdev->dev, "invalid PCI region size\n");
+ err = -EINVAL;
goto err_pci_resource_len_check;
}
diff --git a/drivers/net/ethernet/sfc/selftest.c b/drivers/net/ethernet/sfc/selftest.c
index 10b6173d557d..b605dfd5c7bc 100644
--- a/drivers/net/ethernet/sfc/selftest.c
+++ b/drivers/net/ethernet/sfc/selftest.c
@@ -46,7 +46,7 @@ struct efx_loopback_payload {
struct iphdr ip;
struct udphdr udp;
__be16 iteration;
- const char msg[64];
+ char msg[64];
} __packed;
/* Loopback test source MAC address */
diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
index cd77289c3cfe..623c6ed8764a 100644
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
@@ -150,7 +150,7 @@ struct stmmac_extra_stats {
#define MAC_CSR_H_FRQ_MASK 0x20
#define HASH_TABLE_SIZE 64
-#define PAUSE_TIME 0x200
+#define PAUSE_TIME 0xffff
/* Flow Control defines */
#define FLOW_OFF 0
@@ -357,7 +357,8 @@ struct stmmac_dma_ops {
void (*dump_regs) (void __iomem *ioaddr);
/* Set tx/rx threshold in the csr6 register
* An invalid value enables the store-and-forward mode */
- void (*dma_mode) (void __iomem *ioaddr, int txmode, int rxmode);
+ void (*dma_mode)(void __iomem *ioaddr, int txmode, int rxmode,
+ int rxfifosz);
/* To track extra statistic (if supported) */
void (*dma_diagnostic_fr) (void *data, struct stmmac_extra_stats *x,
void __iomem *ioaddr);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
index 64d8f56a9c17..b3fe0575ff6b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
@@ -172,6 +172,7 @@ enum inter_frame_gap {
/* GMAC FLOW CTRL defines */
#define GMAC_FLOW_CTRL_PT_MASK 0xffff0000 /* Pause Time Mask */
#define GMAC_FLOW_CTRL_PT_SHIFT 16
+#define GMAC_FLOW_CTRL_UP 0x00000008 /* Unicast pause frame enable */
#define GMAC_FLOW_CTRL_RFE 0x00000004 /* Rx Flow Control Enable */
#define GMAC_FLOW_CTRL_TFE 0x00000002 /* Tx Flow Control Enable */
#define GMAC_FLOW_CTRL_FCB_BPA 0x00000001 /* Flow Control Busy ... */
@@ -246,6 +247,56 @@ enum ttc_control {
#define DMA_CONTROL_FEF 0x00000080
#define DMA_CONTROL_FUF 0x00000040
+/* Receive flow control activation field
+ * RFA field in DMA control register, bits 23,10:9
+ */
+#define DMA_CONTROL_RFA_MASK 0x00800600
+
+/* Receive flow control deactivation field
+ * RFD field in DMA control register, bits 22,12:11
+ */
+#define DMA_CONTROL_RFD_MASK 0x00401800
+
+/* RFD and RFA fields are encoded as follows
+ *
+ * Bit Field
+ * 0,00 - Full minus 1KB (only valid when rxfifo >= 4KB and EFC enabled)
+ * 0,01 - Full minus 2KB (only valid when rxfifo >= 4KB and EFC enabled)
+ * 0,10 - Full minus 3KB (only valid when rxfifo >= 4KB and EFC enabled)
+ * 0,11 - Full minus 4KB (only valid when rxfifo > 4KB and EFC enabled)
+ * 1,00 - Full minus 5KB (only valid when rxfifo > 8KB and EFC enabled)
+ * 1,01 - Full minus 6KB (only valid when rxfifo > 8KB and EFC enabled)
+ * 1,10 - Full minus 7KB (only valid when rxfifo > 8KB and EFC enabled)
+ * 1,11 - Reserved
+ *
+ * RFD should always be > RFA for a given FIFO size. RFD == RFA may work,
+ * but packet throughput performance may not be as expected.
+ *
+ * Be sure that bit 3 in GMAC Register 6 is set for Unicast Pause frame
+ * detection (IEEE Specification Requirement, Annex 31B, 31B.1, Pause
+ * Description).
+ *
+ * Be sure that DZPA (bit 7 in Flow Control Register, GMAC Register 6),
+ * is set to 0. This allows pause frames with a quanta of 0 to be sent
+ * as an XOFF message to the link peer.
+ */
+
+#define RFA_FULL_MINUS_1K 0x00000000
+#define RFA_FULL_MINUS_2K 0x00000200
+#define RFA_FULL_MINUS_3K 0x00000400
+#define RFA_FULL_MINUS_4K 0x00000600
+#define RFA_FULL_MINUS_5K 0x00800000
+#define RFA_FULL_MINUS_6K 0x00800200
+#define RFA_FULL_MINUS_7K 0x00800400
+
+#define RFD_FULL_MINUS_1K 0x00000000
+#define RFD_FULL_MINUS_2K 0x00000800
+#define RFD_FULL_MINUS_3K 0x00001000
+#define RFD_FULL_MINUS_4K 0x00001800
+#define RFD_FULL_MINUS_5K 0x00400000
+#define RFD_FULL_MINUS_6K 0x00400800
+#define RFD_FULL_MINUS_7K 0x00401000
+
enum rtc_control {
DMA_CONTROL_RTC_64 = 0x00000000,
DMA_CONTROL_RTC_32 = 0x00000008,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
index 0adcf73cf722..371a669d69fd 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
@@ -201,7 +201,10 @@ static void dwmac1000_flow_ctrl(struct mac_device_info *hw, unsigned int duplex,
unsigned int fc, unsigned int pause_time)
{
void __iomem *ioaddr = hw->pcsr;
- unsigned int flow = 0;
+ /* Set flow such that DZPQ in Mac Register 6 is 0,
+ * and unicast pause detect is enabled.
+ */
+ unsigned int flow = GMAC_FLOW_CTRL_UP;
pr_debug("GMAC Flow-Control:\n");
if (fc & FLOW_RX) {
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
index 59d92e811750..0e8937c1184a 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
@@ -106,8 +106,29 @@ static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb, int mb,
return 0;
}
+static u32 dwmac1000_configure_fc(u32 csr6, int rxfifosz)
+{
+ csr6 &= ~DMA_CONTROL_RFA_MASK;
+ csr6 &= ~DMA_CONTROL_RFD_MASK;
+
+ /* Leave flow control disabled if receive fifo size is less than
+ * 4K or 0. Otherwise, send XOFF when fifo is 1K less than full,
+ * and send XON when 2K less than full.
+ */
+ if (rxfifosz < 4096) {
+ csr6 &= ~DMA_CONTROL_EFC;
+ pr_debug("GMAC: disabling flow control, rxfifo too small(%d)\n",
+ rxfifosz);
+ } else {
+ csr6 |= DMA_CONTROL_EFC;
+ csr6 |= RFA_FULL_MINUS_1K;
+ csr6 |= RFD_FULL_MINUS_2K;
+ }
+ return csr6;
+}
+
static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode,
- int rxmode)
+ int rxmode, int rxfifosz)
{
u32 csr6 = readl(ioaddr + DMA_CONTROL);
@@ -153,6 +174,9 @@ static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode,
csr6 |= DMA_CONTROL_RTC_128;
}
+ /* Configure flow control based on rx fifo size */
+ csr6 = dwmac1000_configure_fc(csr6, rxfifosz);
+
writel(csr6, ioaddr + DMA_CONTROL);
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
index 7d1dce9e7ffc..9d0971c1c2ee 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
@@ -72,7 +72,7 @@ static int dwmac100_dma_init(void __iomem *ioaddr, int pbl, int fb, int mb,
* control register.
*/
static void dwmac100_dma_operation_mode(void __iomem *ioaddr, int txmode,
- int rxmode)
+ int rxmode, int rxfifosz)
{
u32 csr6 = readl(ioaddr + DMA_CONTROL);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 06103cad7c77..05c146f718a3 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1277,8 +1277,10 @@ static void free_dma_desc_resources(struct stmmac_priv *priv)
*/
static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
{
+ int rxfifosz = priv->plat->rx_fifo_size;
+
if (priv->plat->force_thresh_dma_mode)
- priv->hw->dma->dma_mode(priv->ioaddr, tc, tc);
+ priv->hw->dma->dma_mode(priv->ioaddr, tc, tc, rxfifosz);
else if (priv->plat->force_sf_dma_mode || priv->plat->tx_coe) {
/*
* In case of GMAC, SF mode can be enabled
@@ -1287,10 +1289,12 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
* 2) There is no bugged Jumbo frame support
* that needs to not insert csum in the TDES.
*/
- priv->hw->dma->dma_mode(priv->ioaddr, SF_DMA_MODE, SF_DMA_MODE);
+ priv->hw->dma->dma_mode(priv->ioaddr, SF_DMA_MODE, SF_DMA_MODE,
+ rxfifosz);
priv->xstats.threshold = SF_DMA_MODE;
} else
- priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE);
+ priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE,
+ rxfifosz);
}
/**
@@ -1442,6 +1446,7 @@ static void stmmac_tx_err(struct stmmac_priv *priv)
static void stmmac_dma_interrupt(struct stmmac_priv *priv)
{
int status;
+ int rxfifosz = priv->plat->rx_fifo_size;
status = priv->hw->dma->dma_interrupt(priv->ioaddr, &priv->xstats);
if (likely((status & handle_rx)) || (status & handle_tx)) {
@@ -1456,10 +1461,11 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv)
(tc <= 256)) {
tc += 64;
if (priv->plat->force_thresh_dma_mode)
- priv->hw->dma->dma_mode(priv->ioaddr, tc, tc);
+ priv->hw->dma->dma_mode(priv->ioaddr, tc, tc,
+ rxfifosz);
else
priv->hw->dma->dma_mode(priv->ioaddr, tc,
- SF_DMA_MODE);
+ SF_DMA_MODE, rxfifosz);
priv->xstats.threshold = tc;
}
} else if (unlikely(status == tx_hard_error))
@@ -2970,15 +2976,15 @@ int stmmac_dvr_remove(struct net_device *ndev)
priv->hw->dma->stop_tx(priv->ioaddr);
stmmac_set_mac(priv->ioaddr, false);
- if (priv->pcs != STMMAC_PCS_RGMII && priv->pcs != STMMAC_PCS_TBI &&
- priv->pcs != STMMAC_PCS_RTBI)
- stmmac_mdio_unregister(ndev);
netif_carrier_off(ndev);
unregister_netdev(ndev);
if (priv->stmmac_rst)
reset_control_assert(priv->stmmac_rst);
clk_disable_unprepare(priv->pclk);
clk_disable_unprepare(priv->stmmac_clk);
+ if (priv->pcs != STMMAC_PCS_RGMII && priv->pcs != STMMAC_PCS_TBI &&
+ priv->pcs != STMMAC_PCS_RTBI)
+ stmmac_mdio_unregister(ndev);
free_netdev(ndev);
return 0;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index f9b42f11950f..705bbdf93940 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -181,6 +181,10 @@ static int stmmac_probe_config_dt(struct platform_device *pdev,
sizeof(struct stmmac_mdio_bus_data),
GFP_KERNEL);
+ of_property_read_u32(np, "tx-fifo-depth", &plat->tx_fifo_size);
+
+ of_property_read_u32(np, "rx-fifo-depth", &plat->rx_fifo_size);
+
plat->force_sf_dma_mode =
of_property_read_bool(np, "snps,force_sf_dma_mode");
diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c
index 34f846b4bd05..94570aace241 100644
--- a/drivers/net/ifb.c
+++ b/drivers/net/ifb.c
@@ -105,7 +105,7 @@ static void ri_tasklet(unsigned long dev)
if (from & AT_EGRESS) {
dev_queue_xmit(skb);
} else if (from & AT_INGRESS) {
- skb_pull(skb, skb->dev->hard_header_len);
+ skb_pull(skb, skb->mac_len);
netif_receive_skb(skb);
} else
BUG();
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index 9c91ff872485..8c350c5d54ad 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -313,7 +313,7 @@ static rx_handler_result_t macvtap_handle_frame(struct sk_buff **pskb)
*/
if (q->flags & IFF_VNET_HDR)
features |= vlan->tap_features;
- if (netif_needs_gso(dev, skb, features)) {
+ if (netif_needs_gso(skb, features)) {
struct sk_buff *segs = __skb_gso_segment(skb, features, false);
if (IS_ERR(segs))
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index 4c08f98f4484..3f45afd4382e 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -560,7 +560,7 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (unlikely(!netif_carrier_ok(dev) ||
(slots > 1 && !xennet_can_sg(dev)) ||
- netif_needs_gso(dev, skb, netif_skb_features(skb)))) {
+ netif_needs_gso(skb, netif_skb_features(skb)))) {
spin_unlock_irqrestore(&queue->tx_lock, flags);
goto drop;
}
diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
index 09fde58b12e0..0adccbf5c83f 100644
--- a/drivers/platform/Kconfig
+++ b/drivers/platform/Kconfig
@@ -1,6 +1,9 @@
if X86
source "drivers/platform/x86/Kconfig"
endif
+if MIPS
+source "drivers/platform/mips/Kconfig"
+endif
if GOLDFISH
source "drivers/platform/goldfish/Kconfig"
endif
diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
index 3656b7b17b99..ca2692510733 100644
--- a/drivers/platform/Makefile
+++ b/drivers/platform/Makefile
@@ -3,6 +3,7 @@
#
obj-$(CONFIG_X86) += x86/
+obj-$(CONFIG_MIPS) += mips/
obj-$(CONFIG_OLPC) += olpc/
obj-$(CONFIG_GOLDFISH) += goldfish/
obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig
new file mode 100644
index 000000000000..125e569017be
--- /dev/null
+++ b/drivers/platform/mips/Kconfig
@@ -0,0 +1,30 @@
+#
+# MIPS Platform Specific Drivers
+#
+
+menuconfig MIPS_PLATFORM_DEVICES
+ bool "MIPS Platform Specific Device Drivers"
+ default y
+ help
+ Say Y here to get to see options for device drivers of various
+ MIPS platforms, including vendor-specific netbook/laptop/desktop
+ extension and hardware monitor drivers. This option itself does
+ not add any kernel code.
+
+ If you say N, all options in this submenu will be skipped and disabled.
+
+if MIPS_PLATFORM_DEVICES
+
+config MIPS_ACPI
+ bool
+ default y if LOONGSON_MACH3X
+
+config CPU_HWMON
+ tristate "Loongson CPU HWMon Driver"
+ depends on LOONGSON_MACH3X
+ select HWMON
+ default y
+ help
+ Loongson-3A/3B CPU Hwmon (temperature sensor) driver.
+
+endif # MIPS_PLATFORM_DEVICES
diff --git a/drivers/platform/mips/Makefile b/drivers/platform/mips/Makefile
new file mode 100644
index 000000000000..43412849b195
--- /dev/null
+++ b/drivers/platform/mips/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_MIPS_ACPI) += acpi_init.o
+obj-$(CONFIG_CPU_HWMON) += cpu_hwmon.o
diff --git a/drivers/platform/mips/acpi_init.c b/drivers/platform/mips/acpi_init.c
new file mode 100644
index 000000000000..dbdad79ead8f
--- /dev/null
+++ b/drivers/platform/mips/acpi_init.c
@@ -0,0 +1,150 @@
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/export.h>
+
+#define SBX00_ACPI_IO_BASE 0x800
+#define SBX00_ACPI_IO_SIZE 0x100
+
+#define ACPI_PM_EVT_BLK (SBX00_ACPI_IO_BASE + 0x00) /* 4 bytes */
+#define ACPI_PM_CNT_BLK (SBX00_ACPI_IO_BASE + 0x04) /* 2 bytes */
+#define ACPI_PMA_CNT_BLK (SBX00_ACPI_IO_BASE + 0x0F) /* 1 byte */
+#define ACPI_PM_TMR_BLK (SBX00_ACPI_IO_BASE + 0x18) /* 4 bytes */
+#define ACPI_GPE0_BLK (SBX00_ACPI_IO_BASE + 0x10) /* 8 bytes */
+#define ACPI_END (SBX00_ACPI_IO_BASE + 0x80)
+
+#define PM_INDEX 0xCD6
+#define PM_DATA 0xCD7
+#define PM2_INDEX 0xCD0
+#define PM2_DATA 0xCD1
+
+/*
+ * SCI interrupt need acpi space, allocate here
+ */
+
+static int __init register_acpi_resource(void)
+{
+ request_region(SBX00_ACPI_IO_BASE, SBX00_ACPI_IO_SIZE, "acpi");
+ return 0;
+}
+
+static void pmio_write_index(u16 index, u8 reg, u8 value)
+{
+ outb(reg, index);
+ outb(value, index + 1);
+}
+
+static u8 pmio_read_index(u16 index, u8 reg)
+{
+ outb(reg, index);
+ return inb(index + 1);
+}
+
+void pm_iowrite(u8 reg, u8 value)
+{
+ pmio_write_index(PM_INDEX, reg, value);
+}
+EXPORT_SYMBOL(pm_iowrite);
+
+u8 pm_ioread(u8 reg)
+{
+ return pmio_read_index(PM_INDEX, reg);
+}
+EXPORT_SYMBOL(pm_ioread);
+
+void pm2_iowrite(u8 reg, u8 value)
+{
+ pmio_write_index(PM2_INDEX, reg, value);
+}
+EXPORT_SYMBOL(pm2_iowrite);
+
+u8 pm2_ioread(u8 reg)
+{
+ return pmio_read_index(PM2_INDEX, reg);
+}
+EXPORT_SYMBOL(pm2_ioread);
+
+static void acpi_hw_clear_status(void)
+{
+ u16 value;
+
+ /* PMStatus: Clear WakeStatus/PwrBtnStatus */
+ value = inw(ACPI_PM_EVT_BLK);
+ value |= (1 << 8 | 1 << 15);
+ outw(value, ACPI_PM_EVT_BLK);
+
+ /* GPEStatus: Clear all generated events */
+ outl(inl(ACPI_GPE0_BLK), ACPI_GPE0_BLK);
+}
+
+void acpi_registers_setup(void)
+{
+ u32 value;
+
+ /* PM Status Base */
+ pm_iowrite(0x20, ACPI_PM_EVT_BLK & 0xff);
+ pm_iowrite(0x21, ACPI_PM_EVT_BLK >> 8);
+
+ /* PM Control Base */
+ pm_iowrite(0x22, ACPI_PM_CNT_BLK & 0xff);
+ pm_iowrite(0x23, ACPI_PM_CNT_BLK >> 8);
+
+ /* GPM Base */
+ pm_iowrite(0x28, ACPI_GPE0_BLK & 0xff);
+ pm_iowrite(0x29, ACPI_GPE0_BLK >> 8);
+
+ /* ACPI End */
+ pm_iowrite(0x2e, ACPI_END & 0xff);
+ pm_iowrite(0x2f, ACPI_END >> 8);
+
+ /* IO Decode: When AcpiDecodeEnable set, South-Bridge uses the contents
+ * of the PM registers at index 0x20~0x2B to decode ACPI I/O address. */
+ pm_iowrite(0x0e, 1 << 3);
+
+ /* SCI_EN set */
+ outw(1, ACPI_PM_CNT_BLK);
+
+ /* Enable to generate SCI */
+ pm_iowrite(0x10, pm_ioread(0x10) | 1);
+
+ /* GPM3/GPM9 enable */
+ value = inl(ACPI_GPE0_BLK + 4);
+ outl(value | (1 << 14) | (1 << 22), ACPI_GPE0_BLK + 4);
+
+ /* Set GPM9 as input */
+ pm_iowrite(0x8d, pm_ioread(0x8d) & (~(1 << 1)));
+
+ /* Set GPM9 as non-output */
+ pm_iowrite(0x94, pm_ioread(0x94) | (1 << 3));
+
+ /* GPM3 config ACPI trigger SCIOUT */
+ pm_iowrite(0x33, pm_ioread(0x33) & (~(3 << 4)));
+
+ /* GPM9 config ACPI trigger SCIOUT */
+ pm_iowrite(0x3d, pm_ioread(0x3d) & (~(3 << 2)));
+
+ /* GPM3 config falling edge trigger */
+ pm_iowrite(0x37, pm_ioread(0x37) & (~(1 << 6)));
+
+ /* No wait for STPGNT# in ACPI Sx state */
+ pm_iowrite(0x7c, pm_ioread(0x7c) | (1 << 6));
+
+ /* Set GPM3 pull-down enable */
+ value = pm2_ioread(0xf6);
+ value |= ((1 << 7) | (1 << 3));
+ pm2_iowrite(0xf6, value);
+
+ /* Set GPM9 pull-down enable */
+ value = pm2_ioread(0xf8);
+ value |= ((1 << 5) | (1 << 1));
+ pm2_iowrite(0xf8, value);
+}
+
+int __init sbx00_acpi_init(void)
+{
+ register_acpi_resource();
+ acpi_registers_setup();
+ acpi_hw_clear_status();
+
+ return 0;
+}
diff --git a/drivers/platform/mips/cpu_hwmon.c b/drivers/platform/mips/cpu_hwmon.c
new file mode 100644
index 000000000000..0f6c63e17049
--- /dev/null
+++ b/drivers/platform/mips/cpu_hwmon.c
@@ -0,0 +1,207 @@
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/jiffies.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include <loongson.h>
+#include <boot_param.h>
+#include <loongson_hwmon.h>
+
+/*
+ * Loongson-3 series cpu has two sensors inside,
+ * each of them from 0 to 255,
+ * if more than 127, that is dangerous.
+ * here only provide sensor1 data, because it always hot than sensor0
+ */
+int loongson3_cpu_temp(int cpu)
+{
+ u32 reg;
+
+ reg = LOONGSON_CHIPTEMP(cpu);
+ if (loongson_sysconf.cputype == Loongson_3A)
+ reg = (reg >> 8) & 0xff;
+ else if (loongson_sysconf.cputype == Loongson_3B)
+ reg = ((reg >> 8) & 0xff) - 100;
+
+ return (int)reg * 1000;
+}
+
+static struct device *cpu_hwmon_dev;
+
+static ssize_t get_hwmon_name(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, get_hwmon_name, NULL, 0);
+
+static struct attribute *cpu_hwmon_attributes[] = {
+ &sensor_dev_attr_name.dev_attr.attr,
+ NULL
+};
+
+/* Hwmon device attribute group */
+static struct attribute_group cpu_hwmon_attribute_group = {
+ .attrs = cpu_hwmon_attributes,
+};
+
+/* Hwmon device get name */
+static ssize_t get_hwmon_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "cpu-hwmon\n");
+}
+
+static ssize_t get_cpu0_temp(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t get_cpu1_temp(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t cpu0_temp_label(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t cpu1_temp_label(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, get_cpu0_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, cpu0_temp_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, get_cpu1_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, cpu1_temp_label, NULL, 2);
+
+static const struct attribute *hwmon_cputemp1[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_label.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute *hwmon_cputemp2[] = {
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_label.dev_attr.attr,
+ NULL
+};
+
+static ssize_t cpu0_temp_label(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "CPU 0 Temprature\n");
+}
+
+static ssize_t cpu1_temp_label(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "CPU 1 Temprature\n");
+}
+
+static ssize_t get_cpu0_temp(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int value = loongson3_cpu_temp(0);
+ return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t get_cpu1_temp(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int value = loongson3_cpu_temp(1);
+ return sprintf(buf, "%d\n", value);
+}
+
+static int create_sysfs_cputemp_files(struct kobject *kobj)
+{
+ int ret;
+
+ ret = sysfs_create_files(kobj, hwmon_cputemp1);
+ if (ret)
+ goto sysfs_create_temp1_fail;
+
+ if (loongson_sysconf.nr_cpus <= loongson_sysconf.cores_per_package)
+ return 0;
+
+ ret = sysfs_create_files(kobj, hwmon_cputemp2);
+ if (ret)
+ goto sysfs_create_temp2_fail;
+
+ return 0;
+
+sysfs_create_temp2_fail:
+ sysfs_remove_files(kobj, hwmon_cputemp1);
+
+sysfs_create_temp1_fail:
+ return -1;
+}
+
+static void remove_sysfs_cputemp_files(struct kobject *kobj)
+{
+ sysfs_remove_files(&cpu_hwmon_dev->kobj, hwmon_cputemp1);
+
+ if (loongson_sysconf.nr_cpus > loongson_sysconf.cores_per_package)
+ sysfs_remove_files(&cpu_hwmon_dev->kobj, hwmon_cputemp2);
+}
+
+#define CPU_THERMAL_THRESHOLD 90000
+static struct delayed_work thermal_work;
+
+static void do_thermal_timer(struct work_struct *work)
+{
+ int value = loongson3_cpu_temp(0);
+ if (value <= CPU_THERMAL_THRESHOLD)
+ schedule_delayed_work(&thermal_work, msecs_to_jiffies(5000));
+ else
+ orderly_poweroff(true);
+}
+
+static int __init loongson_hwmon_init(void)
+{
+ int ret;
+
+ pr_info("Loongson Hwmon Enter...\n");
+
+ cpu_hwmon_dev = hwmon_device_register(NULL);
+ if (IS_ERR(cpu_hwmon_dev)) {
+ ret = -ENOMEM;
+ pr_err("hwmon_device_register fail!\n");
+ goto fail_hwmon_device_register;
+ }
+
+ ret = sysfs_create_group(&cpu_hwmon_dev->kobj,
+ &cpu_hwmon_attribute_group);
+ if (ret) {
+ pr_err("fail to create loongson hwmon!\n");
+ goto fail_sysfs_create_group_hwmon;
+ }
+
+ ret = create_sysfs_cputemp_files(&cpu_hwmon_dev->kobj);
+ if (ret) {
+ pr_err("fail to create cpu temprature interface!\n");
+ goto fail_create_sysfs_cputemp_files;
+ }
+
+ INIT_DEFERRABLE_WORK(&thermal_work, do_thermal_timer);
+ schedule_delayed_work(&thermal_work, msecs_to_jiffies(20000));
+
+ return ret;
+
+fail_create_sysfs_cputemp_files:
+ sysfs_remove_group(&cpu_hwmon_dev->kobj,
+ &cpu_hwmon_attribute_group);
+
+fail_sysfs_create_group_hwmon:
+ hwmon_device_unregister(cpu_hwmon_dev);
+
+fail_hwmon_device_register:
+ return ret;
+}
+
+static void __exit loongson_hwmon_exit(void)
+{
+ cancel_delayed_work_sync(&thermal_work);
+ remove_sysfs_cputemp_files(&cpu_hwmon_dev->kobj);
+ sysfs_remove_group(&cpu_hwmon_dev->kobj,
+ &cpu_hwmon_attribute_group);
+ hwmon_device_unregister(cpu_hwmon_dev);
+}
+
+module_init(loongson_hwmon_init);
+module_exit(loongson_hwmon_exit);
+
+MODULE_AUTHOR("Yu Xiang <xiangy@lemote.com>");
+MODULE_AUTHOR("Huacai Chen <chenhc@lemote.com>");
+MODULE_DESCRIPTION("Loongson CPU Hwmon driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig
index 75b3603906c1..f0d22cdb51cd 100644
--- a/drivers/ssb/Kconfig
+++ b/drivers/ssb/Kconfig
@@ -130,6 +130,7 @@ config SSB_DRIVER_MIPS
bool "SSB Broadcom MIPS core driver"
depends on SSB && MIPS
select SSB_SERIAL
+ select SSB_SFLASH
help
Driver for the Sonics Silicon Backplane attached
Broadcom MIPS core.
diff --git a/drivers/ssb/driver_chipcommon_pmu.c b/drivers/ssb/driver_chipcommon_pmu.c
index 1173a091b402..09428412139e 100644
--- a/drivers/ssb/driver_chipcommon_pmu.c
+++ b/drivers/ssb/driver_chipcommon_pmu.c
@@ -14,7 +14,7 @@
#include <linux/delay.h>
#include <linux/export.h>
#ifdef CONFIG_BCM47XX
-#include <bcm47xx_nvram.h>
+#include <linux/bcm47xx_nvram.h>
#endif
#include "ssb_private.h"
diff --git a/drivers/ssb/driver_mipscore.c b/drivers/ssb/driver_mipscore.c
index 7b986f9f213f..f87efef42252 100644
--- a/drivers/ssb/driver_mipscore.c
+++ b/drivers/ssb/driver_mipscore.c
@@ -16,7 +16,7 @@
#include <linux/serial_reg.h>
#include <linux/time.h>
#ifdef CONFIG_BCM47XX
-#include <bcm47xx_nvram.h>
+#include <linux/bcm47xx_nvram.h>
#endif
#include "ssb_private.h"
diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c
index b7a7854d3f7e..5b9ac1f6d6f0 100644
--- a/drivers/staging/octeon/ethernet-tx.c
+++ b/drivers/staging/octeon/ethernet-tx.c
@@ -274,6 +274,9 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
/* Build the PKO command */
pko_command.u64 = 0;
+#ifdef __LITTLE_ENDIAN
+ pko_command.s.le = 1;
+#endif
pko_command.s.n2 = 1; /* Don't pollute L2 with the outgoing packet */
pko_command.s.segs = 1;
pko_command.s.total_bytes = skb->len;
@@ -410,7 +413,7 @@ dont_put_skbuff_in_hw:
/* Check if we can use the hardware checksumming */
if (USE_HW_TCPUDP_CHECKSUM && (skb->protocol == htons(ETH_P_IP)) &&
(ip_hdr(skb)->version == 4) && (ip_hdr(skb)->ihl == 5) &&
- ((ip_hdr(skb)->frag_off == 0) || (ip_hdr(skb)->frag_off == 1 << 14))
+ ((ip_hdr(skb)->frag_off == 0) || (ip_hdr(skb)->frag_off == htons(1 << 14)))
&& ((ip_hdr(skb)->protocol == IPPROTO_TCP)
|| (ip_hdr(skb)->protocol == IPPROTO_UDP))) {
/* Use hardware checksum calc */
diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c
index f539d82f2f11..fbbe866485c7 100644
--- a/drivers/staging/octeon/ethernet.c
+++ b/drivers/staging/octeon/ethernet.c
@@ -170,6 +170,16 @@ static void cvm_oct_configure_common_hw(void)
cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL,
CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
+#ifdef __LITTLE_ENDIAN
+ {
+ union cvmx_ipd_ctl_status ipd_ctl_status;
+ ipd_ctl_status.u64 = cvmx_read_csr(CVMX_IPD_CTL_STATUS);
+ ipd_ctl_status.s.pkt_lend = 1;
+ ipd_ctl_status.s.wqe_lend = 1;
+ cvmx_write_csr(CVMX_IPD_CTL_STATUS, ipd_ctl_status.u64);
+ }
+#endif
+
if (USE_RED)
cvmx_helper_setup_red(num_packet_buffers / 4,
num_packet_buffers / 8);
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index b24aa010f68c..c01f45095877 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -419,4 +419,51 @@ config DA_CONSOLE
help
This enables a console on a Dash channel.
+config MIPS_EJTAG_FDC_TTY
+ bool "MIPS EJTAG Fast Debug Channel TTY"
+ depends on MIPS_CDMM
+ help
+ This enables a TTY and console on the MIPS EJTAG Fast Debug Channels,
+ if they are present. This can be useful when working with an EJTAG
+ probe which supports it, to get console output and a login prompt via
+ EJTAG without needing to connect a serial cable.
+
+ TTY devices are named e.g. ttyFDC3c2 (for FDC channel 2 of the FDC on
+ CPU3).
+
+ The console can be enabled with console=fdc1 (for FDC channel 1 on all
+ CPUs). Do not use the console unless there is a debug probe attached
+ to drain the FDC TX FIFO.
+
+ If unsure, say N.
+
+config MIPS_EJTAG_FDC_EARLYCON
+ bool "Early FDC console"
+ depends on MIPS_EJTAG_FDC_TTY
+ help
+ This registers a console on FDC channel 1 very early during boot (from
+ MIPS arch code). This is useful for bring-up and debugging early boot
+ issues.
+
+ Do not enable unless there is a debug probe attached to drain the FDC
+ TX FIFO.
+
+ If unsure, say N.
+
+config MIPS_EJTAG_FDC_KGDB
+ bool "Use KGDB over an FDC channel"
+ depends on MIPS_EJTAG_FDC_TTY && KGDB
+ default y
+ help
+ This enables the use of KGDB over an FDC channel, allowing KGDB to be
+ used remotely or when a serial port isn't available.
+
+config MIPS_EJTAG_FDC_KGDB_CHAN
+ int "KGDB FDC channel"
+ depends on MIPS_EJTAG_FDC_KGDB
+ range 2 15
+ default 3
+ help
+ FDC channel number to use for KGDB.
+
endif # TTY
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index 58ad1c05b7f8..5817e2397463 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile
@@ -29,5 +29,6 @@ obj-$(CONFIG_SYNCLINK) += synclink.o
obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o
obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o
obj-$(CONFIG_DA_TTY) += metag_da.o
+obj-$(CONFIG_MIPS_EJTAG_FDC_TTY) += mips_ejtag_fdc.o
obj-y += ipwireless/
diff --git a/drivers/tty/mips_ejtag_fdc.c b/drivers/tty/mips_ejtag_fdc.c
new file mode 100644
index 000000000000..04d9e23d1ee1
--- /dev/null
+++ b/drivers/tty/mips_ejtag_fdc.c
@@ -0,0 +1,1303 @@
+/*
+ * TTY driver for MIPS EJTAG Fast Debug Channels.
+ *
+ * Copyright (C) 2007-2015 Imagination Technologies Ltd
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for more
+ * details.
+ */
+
+#include <linux/atomic.h>
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/kgdb.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/uaccess.h>
+
+#include <asm/cdmm.h>
+#include <asm/irq.h>
+
+/* Register offsets */
+#define REG_FDACSR 0x00 /* FDC Access Control and Status Register */
+#define REG_FDCFG 0x08 /* FDC Configuration Register */
+#define REG_FDSTAT 0x10 /* FDC Status Register */
+#define REG_FDRX 0x18 /* FDC Receive Register */
+#define REG_FDTX(N) (0x20+0x8*(N)) /* FDC Transmit Register n (0..15) */
+
+/* Register fields */
+
+#define REG_FDCFG_TXINTTHRES_SHIFT 18
+#define REG_FDCFG_TXINTTHRES (0x3 << REG_FDCFG_TXINTTHRES_SHIFT)
+#define REG_FDCFG_TXINTTHRES_DISABLED (0x0 << REG_FDCFG_TXINTTHRES_SHIFT)
+#define REG_FDCFG_TXINTTHRES_EMPTY (0x1 << REG_FDCFG_TXINTTHRES_SHIFT)
+#define REG_FDCFG_TXINTTHRES_NOTFULL (0x2 << REG_FDCFG_TXINTTHRES_SHIFT)
+#define REG_FDCFG_TXINTTHRES_NEAREMPTY (0x3 << REG_FDCFG_TXINTTHRES_SHIFT)
+#define REG_FDCFG_RXINTTHRES_SHIFT 16
+#define REG_FDCFG_RXINTTHRES (0x3 << REG_FDCFG_RXINTTHRES_SHIFT)
+#define REG_FDCFG_RXINTTHRES_DISABLED (0x0 << REG_FDCFG_RXINTTHRES_SHIFT)
+#define REG_FDCFG_RXINTTHRES_FULL (0x1 << REG_FDCFG_RXINTTHRES_SHIFT)
+#define REG_FDCFG_RXINTTHRES_NOTEMPTY (0x2 << REG_FDCFG_RXINTTHRES_SHIFT)
+#define REG_FDCFG_RXINTTHRES_NEARFULL (0x3 << REG_FDCFG_RXINTTHRES_SHIFT)
+#define REG_FDCFG_TXFIFOSIZE_SHIFT 8
+#define REG_FDCFG_TXFIFOSIZE (0xff << REG_FDCFG_TXFIFOSIZE_SHIFT)
+#define REG_FDCFG_RXFIFOSIZE_SHIFT 0
+#define REG_FDCFG_RXFIFOSIZE (0xff << REG_FDCFG_RXFIFOSIZE_SHIFT)
+
+#define REG_FDSTAT_TXCOUNT_SHIFT 24
+#define REG_FDSTAT_TXCOUNT (0xff << REG_FDSTAT_TXCOUNT_SHIFT)
+#define REG_FDSTAT_RXCOUNT_SHIFT 16
+#define REG_FDSTAT_RXCOUNT (0xff << REG_FDSTAT_RXCOUNT_SHIFT)
+#define REG_FDSTAT_RXCHAN_SHIFT 4
+#define REG_FDSTAT_RXCHAN (0xf << REG_FDSTAT_RXCHAN_SHIFT)
+#define REG_FDSTAT_RXE BIT(3) /* Rx Empty */
+#define REG_FDSTAT_RXF BIT(2) /* Rx Full */
+#define REG_FDSTAT_TXE BIT(1) /* Tx Empty */
+#define REG_FDSTAT_TXF BIT(0) /* Tx Full */
+
+/* Default channel for the early console */
+#define CONSOLE_CHANNEL 1
+
+#define NUM_TTY_CHANNELS 16
+
+#define RX_BUF_SIZE 1024
+
+/*
+ * When the IRQ is unavailable, the FDC state must be polled for incoming data
+ * and space becoming available in TX FIFO.
+ */
+#define FDC_TTY_POLL (HZ / 50)
+
+struct mips_ejtag_fdc_tty;
+
+/**
+ * struct mips_ejtag_fdc_tty_port - Wrapper struct for FDC tty_port.
+ * @port: TTY port data
+ * @driver: TTY driver.
+ * @rx_lock: Lock for rx_buf.
+ * This protects between the hard interrupt and user
+ * context. It's also held during read SWITCH operations.
+ * @rx_buf: Read buffer.
+ * @xmit_lock: Lock for xmit_*, and port.xmit_buf.
+ * This protects between user context and kernel thread.
+ * It is used from chars_in_buffer()/write_room() TTY
+ * callbacks which are used during wait operations, so a
+ * mutex is unsuitable.
+ * @xmit_cnt: Size of xmit buffer contents.
+ * @xmit_head: Head of xmit buffer where data is written.
+ * @xmit_tail: Tail of xmit buffer where data is read.
+ * @xmit_empty: Completion for xmit buffer being empty.
+ */
+struct mips_ejtag_fdc_tty_port {
+ struct tty_port port;
+ struct mips_ejtag_fdc_tty *driver;
+ raw_spinlock_t rx_lock;
+ void *rx_buf;
+ spinlock_t xmit_lock;
+ unsigned int xmit_cnt;
+ unsigned int xmit_head;
+ unsigned int xmit_tail;
+ struct completion xmit_empty;
+};
+
+/**
+ * struct mips_ejtag_fdc_tty - Driver data for FDC as a whole.
+ * @dev: FDC device (for dev_*() logging).
+ * @driver: TTY driver.
+ * @cpu: CPU number for this FDC.
+ * @fdc_name: FDC name (not for base of channel names).
+ * @driver_name: Base of driver name.
+ * @ports: Per-channel data.
+ * @waitqueue: Wait queue for waiting for TX data, or for space in TX
+ * FIFO.
+ * @lock: Lock to protect FDCFG (interrupt enable).
+ * @thread: KThread for writing out data to FDC.
+ * @reg: FDC registers.
+ * @tx_fifo: TX FIFO size.
+ * @xmit_size: Size of each port's xmit buffer.
+ * @xmit_total: Total number of bytes (from all ports) to transmit.
+ * @xmit_next: Next port number to transmit from (round robin).
+ * @xmit_full: Indicates TX FIFO is full, we're waiting for space.
+ * @irq: IRQ number (negative if no IRQ).
+ * @removing: Indicates the device is being removed and @poll_timer
+ * should not be restarted.
+ * @poll_timer: Timer for polling for interrupt events when @irq < 0.
+ * @sysrq_pressed: Whether the magic sysrq key combination has been
+ * detected. See mips_ejtag_fdc_handle().
+ */
+struct mips_ejtag_fdc_tty {
+ struct device *dev;
+ struct tty_driver *driver;
+ unsigned int cpu;
+ char fdc_name[16];
+ char driver_name[16];
+ struct mips_ejtag_fdc_tty_port ports[NUM_TTY_CHANNELS];
+ wait_queue_head_t waitqueue;
+ raw_spinlock_t lock;
+ struct task_struct *thread;
+
+ void __iomem *reg;
+ u8 tx_fifo;
+
+ unsigned int xmit_size;
+ atomic_t xmit_total;
+ unsigned int xmit_next;
+ bool xmit_full;
+
+ int irq;
+ bool removing;
+ struct timer_list poll_timer;
+
+#ifdef CONFIG_MAGIC_SYSRQ
+ bool sysrq_pressed;
+#endif
+};
+
+/* Hardware access */
+
+static inline void mips_ejtag_fdc_write(struct mips_ejtag_fdc_tty *priv,
+ unsigned int offs, unsigned int data)
+{
+ iowrite32(data, priv->reg + offs);
+}
+
+static inline unsigned int mips_ejtag_fdc_read(struct mips_ejtag_fdc_tty *priv,
+ unsigned int offs)
+{
+ return ioread32(priv->reg + offs);
+}
+
+/* Encoding of byte stream in FDC words */
+
+/**
+ * struct fdc_word - FDC word encoding some number of bytes of data.
+ * @word: Raw FDC word.
+ * @bytes: Number of bytes encoded by @word.
+ */
+struct fdc_word {
+ u32 word;
+ unsigned int bytes;
+};
+
+/*
+ * This is a compact encoding which allows every 1 byte, 2 byte, and 3 byte
+ * sequence to be encoded in a single word, while allowing the majority of 4
+ * byte sequences (including all ASCII and common binary data) to be encoded in
+ * a single word too.
+ * _______________________ _____________
+ * | FDC Word | |
+ * |31-24|23-16|15-8 | 7-0 | Bytes |
+ * |_____|_____|_____|_____|_____________|
+ * | | | | | |
+ * |0x80 |0x80 |0x80 | WW | WW |
+ * |0x81 |0x81 | XX | WW | WW XX |
+ * |0x82 | YY | XX | WW | WW XX YY |
+ * | ZZ | YY | XX | WW | WW XX YY ZZ |
+ * |_____|_____|_____|_____|_____________|
+ *
+ * Note that the 4-byte encoding can only be used where none of the other 3
+ * encodings match, otherwise it must fall back to the 3 byte encoding.
+ */
+
+/* ranges >= 1 && sizes[0] >= 1 */
+static struct fdc_word mips_ejtag_fdc_encode(const char **ptrs,
+ unsigned int *sizes,
+ unsigned int ranges)
+{
+ struct fdc_word word = { 0, 0 };
+ const char **ptrs_end = ptrs + ranges;
+
+ for (; ptrs < ptrs_end; ++ptrs) {
+ const char *ptr = *(ptrs++);
+ const char *end = ptr + *(sizes++);
+
+ for (; ptr < end; ++ptr) {
+ word.word |= (u8)*ptr << (8*word.bytes);
+ ++word.bytes;
+ if (word.bytes == 4)
+ goto done;
+ }
+ }
+done:
+ /* Choose the appropriate encoding */
+ switch (word.bytes) {
+ case 4:
+ /* 4 byte encoding, but don't match the 1-3 byte encodings */
+ if ((word.word >> 8) != 0x808080 &&
+ (word.word >> 16) != 0x8181 &&
+ (word.word >> 24) != 0x82)
+ break;
+ /* Fall back to a 3 byte encoding */
+ word.bytes = 3;
+ word.word &= 0x00ffffff;
+ case 3:
+ /* 3 byte encoding */
+ word.word |= 0x82000000;
+ break;
+ case 2:
+ /* 2 byte encoding */
+ word.word |= 0x81810000;
+ break;
+ case 1:
+ /* 1 byte encoding */
+ word.word |= 0x80808000;
+ break;
+ }
+ return word;
+}
+
+static unsigned int mips_ejtag_fdc_decode(u32 word, char *buf)
+{
+ buf[0] = (u8)word;
+ word >>= 8;
+ if (word == 0x808080)
+ return 1;
+ buf[1] = (u8)word;
+ word >>= 8;
+ if (word == 0x8181)
+ return 2;
+ buf[2] = (u8)word;
+ word >>= 8;
+ if (word == 0x82)
+ return 3;
+ buf[3] = (u8)word;
+ return 4;
+}
+
+/* Console operations */
+
+/**
+ * struct mips_ejtag_fdc_console - Wrapper struct for FDC consoles.
+ * @cons: Console object.
+ * @tty_drv: TTY driver associated with this console.
+ * @lock: Lock to protect concurrent access to other fields.
+ * This is raw because it may be used very early.
+ * @initialised: Whether the console is initialised.
+ * @regs: Registers base address for each CPU.
+ */
+struct mips_ejtag_fdc_console {
+ struct console cons;
+ struct tty_driver *tty_drv;
+ raw_spinlock_t lock;
+ bool initialised;
+ void __iomem *regs[NR_CPUS];
+};
+
+/* Low level console write shared by early console and normal console */
+static void mips_ejtag_fdc_console_write(struct console *c, const char *s,
+ unsigned int count)
+{
+ struct mips_ejtag_fdc_console *cons =
+ container_of(c, struct mips_ejtag_fdc_console, cons);
+ void __iomem *regs;
+ struct fdc_word word;
+ unsigned long flags;
+ unsigned int i, buf_len, cpu;
+ bool done_cr = false;
+ char buf[4];
+ const char *buf_ptr = buf;
+ /* Number of bytes of input data encoded up to each byte in buf */
+ u8 inc[4];
+
+ local_irq_save(flags);
+ cpu = smp_processor_id();
+ regs = cons->regs[cpu];
+ /* First console output on this CPU? */
+ if (!regs) {
+ regs = mips_cdmm_early_probe(0xfd);
+ cons->regs[cpu] = regs;
+ }
+ /* Already tried and failed to find FDC on this CPU? */
+ if (IS_ERR(regs))
+ goto out;
+ while (count) {
+ /*
+ * Copy the next few characters to a buffer so we can inject
+ * carriage returns before newlines.
+ */
+ for (buf_len = 0, i = 0; buf_len < 4 && i < count; ++buf_len) {
+ if (s[i] == '\n' && !done_cr) {
+ buf[buf_len] = '\r';
+ done_cr = true;
+ } else {
+ buf[buf_len] = s[i];
+ done_cr = false;
+ ++i;
+ }
+ inc[buf_len] = i;
+ }
+ word = mips_ejtag_fdc_encode(&buf_ptr, &buf_len, 1);
+ count -= inc[word.bytes - 1];
+ s += inc[word.bytes - 1];
+
+ /* Busy wait until there's space in fifo */
+ while (ioread32(regs + REG_FDSTAT) & REG_FDSTAT_TXF)
+ ;
+ iowrite32(word.word, regs + REG_FDTX(c->index));
+ }
+out:
+ local_irq_restore(flags);
+}
+
+static struct tty_driver *mips_ejtag_fdc_console_device(struct console *c,
+ int *index)
+{
+ struct mips_ejtag_fdc_console *cons =
+ container_of(c, struct mips_ejtag_fdc_console, cons);
+
+ *index = c->index;
+ return cons->tty_drv;
+}
+
+/* Initialise an FDC console (early or normal */
+static int __init mips_ejtag_fdc_console_init(struct mips_ejtag_fdc_console *c)
+{
+ void __iomem *regs;
+ unsigned long flags;
+ int ret = 0;
+
+ raw_spin_lock_irqsave(&c->lock, flags);
+ /* Don't init twice */
+ if (c->initialised)
+ goto out;
+ /* Look for the FDC device */
+ regs = mips_cdmm_early_probe(0xfd);
+ if (IS_ERR(regs)) {
+ ret = PTR_ERR(regs);
+ goto out;
+ }
+
+ c->initialised = true;
+ c->regs[smp_processor_id()] = regs;
+ register_console(&c->cons);
+out:
+ raw_spin_unlock_irqrestore(&c->lock, flags);
+ return ret;
+}
+
+static struct mips_ejtag_fdc_console mips_ejtag_fdc_con = {
+ .cons = {
+ .name = "fdc",
+ .write = mips_ejtag_fdc_console_write,
+ .device = mips_ejtag_fdc_console_device,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ },
+ .lock = __RAW_SPIN_LOCK_UNLOCKED(mips_ejtag_fdc_con.lock),
+};
+
+/* TTY RX/TX operations */
+
+/**
+ * mips_ejtag_fdc_put_chan() - Write out a block of channel data.
+ * @priv: Pointer to driver private data.
+ * @chan: Channel number.
+ *
+ * Write a single block of data out to the debug adapter. If the circular buffer
+ * is wrapped then only the first block is written.
+ *
+ * Returns: The number of bytes that were written.
+ */
+static unsigned int mips_ejtag_fdc_put_chan(struct mips_ejtag_fdc_tty *priv,
+ unsigned int chan)
+{
+ struct mips_ejtag_fdc_tty_port *dport;
+ struct tty_struct *tty;
+ const char *ptrs[2];
+ unsigned int sizes[2] = { 0 };
+ struct fdc_word word = { .bytes = 0 };
+ unsigned long flags;
+
+ dport = &priv->ports[chan];
+ spin_lock(&dport->xmit_lock);
+ if (dport->xmit_cnt) {
+ ptrs[0] = dport->port.xmit_buf + dport->xmit_tail;
+ sizes[0] = min_t(unsigned int,
+ priv->xmit_size - dport->xmit_tail,
+ dport->xmit_cnt);
+ ptrs[1] = dport->port.xmit_buf;
+ sizes[1] = dport->xmit_cnt - sizes[0];
+ word = mips_ejtag_fdc_encode(ptrs, sizes, 1 + !!sizes[1]);
+
+ dev_dbg(priv->dev, "%s%u: out %08x: \"%*pE%*pE\"\n",
+ priv->driver_name, chan, word.word,
+ min_t(int, word.bytes, sizes[0]), ptrs[0],
+ max_t(int, 0, word.bytes - sizes[0]), ptrs[1]);
+
+ local_irq_save(flags);
+ /* Maybe we raced with the console and TX FIFO is full */
+ if (mips_ejtag_fdc_read(priv, REG_FDSTAT) & REG_FDSTAT_TXF)
+ word.bytes = 0;
+ else
+ mips_ejtag_fdc_write(priv, REG_FDTX(chan), word.word);
+ local_irq_restore(flags);
+
+ dport->xmit_cnt -= word.bytes;
+ if (!dport->xmit_cnt) {
+ /* Reset pointers to avoid wraps */
+ dport->xmit_head = 0;
+ dport->xmit_tail = 0;
+ complete(&dport->xmit_empty);
+ } else {
+ dport->xmit_tail += word.bytes;
+ if (dport->xmit_tail >= priv->xmit_size)
+ dport->xmit_tail -= priv->xmit_size;
+ }
+ atomic_sub(word.bytes, &priv->xmit_total);
+ }
+ spin_unlock(&dport->xmit_lock);
+
+ /* If we've made more data available, wake up tty */
+ if (sizes[0] && word.bytes) {
+ tty = tty_port_tty_get(&dport->port);
+ if (tty) {
+ tty_wakeup(tty);
+ tty_kref_put(tty);
+ }
+ }
+
+ return word.bytes;
+}
+
+/**
+ * mips_ejtag_fdc_put() - Kernel thread to write out channel data to FDC.
+ * @arg: Driver pointer.
+ *
+ * This kernel thread runs while @priv->xmit_total != 0, and round robins the
+ * channels writing out blocks of buffered data to the FDC TX FIFO.
+ */
+static int mips_ejtag_fdc_put(void *arg)
+{
+ struct mips_ejtag_fdc_tty *priv = arg;
+ struct mips_ejtag_fdc_tty_port *dport;
+ unsigned int ret;
+ u32 cfg;
+
+ __set_current_state(TASK_RUNNING);
+ while (!kthread_should_stop()) {
+ /* Wait for data to actually write */
+ wait_event_interruptible(priv->waitqueue,
+ atomic_read(&priv->xmit_total) ||
+ kthread_should_stop());
+ if (kthread_should_stop())
+ break;
+
+ /* Wait for TX FIFO space to write data */
+ raw_spin_lock_irq(&priv->lock);
+ if (mips_ejtag_fdc_read(priv, REG_FDSTAT) & REG_FDSTAT_TXF) {
+ priv->xmit_full = true;
+ if (priv->irq >= 0) {
+ /* Enable TX interrupt */
+ cfg = mips_ejtag_fdc_read(priv, REG_FDCFG);
+ cfg &= ~REG_FDCFG_TXINTTHRES;
+ cfg |= REG_FDCFG_TXINTTHRES_NOTFULL;
+ mips_ejtag_fdc_write(priv, REG_FDCFG, cfg);
+ }
+ }
+ raw_spin_unlock_irq(&priv->lock);
+ wait_event_interruptible(priv->waitqueue,
+ !(mips_ejtag_fdc_read(priv, REG_FDSTAT)
+ & REG_FDSTAT_TXF) ||
+ kthread_should_stop());
+ if (kthread_should_stop())
+ break;
+
+ /* Find next channel with data to output */
+ for (;;) {
+ dport = &priv->ports[priv->xmit_next];
+ spin_lock(&dport->xmit_lock);
+ ret = dport->xmit_cnt;
+ spin_unlock(&dport->xmit_lock);
+ if (ret)
+ break;
+ /* Round robin */
+ ++priv->xmit_next;
+ if (priv->xmit_next >= NUM_TTY_CHANNELS)
+ priv->xmit_next = 0;
+ }
+
+ /* Try writing data to the chosen channel */
+ ret = mips_ejtag_fdc_put_chan(priv, priv->xmit_next);
+
+ /*
+ * If anything was output, move on to the next channel so as not
+ * to starve other channels.
+ */
+ if (ret) {
+ ++priv->xmit_next;
+ if (priv->xmit_next >= NUM_TTY_CHANNELS)
+ priv->xmit_next = 0;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * mips_ejtag_fdc_handle() - Handle FDC events.
+ * @priv: Pointer to driver private data.
+ *
+ * Handle FDC events, such as new incoming data which needs draining out of the
+ * RX FIFO and feeding into the appropriate TTY ports, and space becoming
+ * available in the TX FIFO which would allow more data to be written out.
+ */
+static void mips_ejtag_fdc_handle(struct mips_ejtag_fdc_tty *priv)
+{
+ struct mips_ejtag_fdc_tty_port *dport;
+ unsigned int stat, channel, data, cfg, i, flipped;
+ int len;
+ char buf[4];
+
+ for (;;) {
+ /* Find which channel the next FDC word is destined for */
+ stat = mips_ejtag_fdc_read(priv, REG_FDSTAT);
+ if (stat & REG_FDSTAT_RXE)
+ break;
+ channel = (stat & REG_FDSTAT_RXCHAN) >> REG_FDSTAT_RXCHAN_SHIFT;
+ dport = &priv->ports[channel];
+
+ /* Read out the FDC word, decode it, and pass to tty layer */
+ raw_spin_lock(&dport->rx_lock);
+ data = mips_ejtag_fdc_read(priv, REG_FDRX);
+
+ len = mips_ejtag_fdc_decode(data, buf);
+ dev_dbg(priv->dev, "%s%u: in %08x: \"%*pE\"\n",
+ priv->driver_name, channel, data, len, buf);
+
+ flipped = 0;
+ for (i = 0; i < len; ++i) {
+#ifdef CONFIG_MAGIC_SYSRQ
+#ifdef CONFIG_MIPS_EJTAG_FDC_KGDB
+ /* Support just Ctrl+C with KGDB channel */
+ if (channel == CONFIG_MIPS_EJTAG_FDC_KGDB_CHAN) {
+ if (buf[i] == '\x03') { /* ^C */
+ handle_sysrq('g');
+ continue;
+ }
+ }
+#endif
+ /* Support Ctrl+O for console channel */
+ if (channel == mips_ejtag_fdc_con.cons.index) {
+ if (buf[i] == '\x0f') { /* ^O */
+ priv->sysrq_pressed =
+ !priv->sysrq_pressed;
+ if (priv->sysrq_pressed)
+ continue;
+ } else if (priv->sysrq_pressed) {
+ handle_sysrq(buf[i]);
+ priv->sysrq_pressed = false;
+ continue;
+ }
+ }
+#endif /* CONFIG_MAGIC_SYSRQ */
+
+ /* Check the port isn't being shut down */
+ if (!dport->rx_buf)
+ continue;
+
+ flipped += tty_insert_flip_char(&dport->port, buf[i],
+ TTY_NORMAL);
+ }
+ if (flipped)
+ tty_flip_buffer_push(&dport->port);
+
+ raw_spin_unlock(&dport->rx_lock);
+ }
+
+ /* If TX FIFO no longer full we may be able to write more data */
+ raw_spin_lock(&priv->lock);
+ if (priv->xmit_full && !(stat & REG_FDSTAT_TXF)) {
+ priv->xmit_full = false;
+
+ /* Disable TX interrupt */
+ cfg = mips_ejtag_fdc_read(priv, REG_FDCFG);
+ cfg &= ~REG_FDCFG_TXINTTHRES;
+ cfg |= REG_FDCFG_TXINTTHRES_DISABLED;
+ mips_ejtag_fdc_write(priv, REG_FDCFG, cfg);
+
+ /* Wait the kthread so it can try writing more data */
+ wake_up_interruptible(&priv->waitqueue);
+ }
+ raw_spin_unlock(&priv->lock);
+}
+
+/**
+ * mips_ejtag_fdc_isr() - Interrupt handler.
+ * @irq: IRQ number.
+ * @dev_id: Pointer to driver private data.
+ *
+ * This is the interrupt handler, used when interrupts are enabled.
+ *
+ * It simply triggers the common FDC handler code.
+ *
+ * Returns: IRQ_HANDLED if an FDC interrupt was pending.
+ * IRQ_NONE otherwise.
+ */
+static irqreturn_t mips_ejtag_fdc_isr(int irq, void *dev_id)
+{
+ struct mips_ejtag_fdc_tty *priv = dev_id;
+
+ /*
+ * We're not using proper per-cpu IRQs, so we must be careful not to
+ * handle IRQs on CPUs we're not interested in.
+ *
+ * Ideally proper per-cpu IRQ handlers could be used, but that doesn't
+ * fit well with the whole sharing of the main CPU IRQ lines. When we
+ * have something with a GIC that routes the FDC IRQs (i.e. no sharing
+ * between handlers) then support could be added more easily.
+ */
+ if (smp_processor_id() != priv->cpu)
+ return IRQ_NONE;
+
+ /* If no FDC interrupt pending, it wasn't for us */
+ if (!(read_c0_cause() & CAUSEF_FDCI))
+ return IRQ_NONE;
+
+ mips_ejtag_fdc_handle(priv);
+ return IRQ_HANDLED;
+}
+
+/**
+ * mips_ejtag_fdc_tty_timer() - Poll FDC for incoming data.
+ * @opaque: Pointer to driver private data.
+ *
+ * This is the timer handler for when interrupts are disabled and polling the
+ * FDC state is required.
+ *
+ * It simply triggers the common FDC handler code and arranges for further
+ * polling.
+ */
+static void mips_ejtag_fdc_tty_timer(unsigned long opaque)
+{
+ struct mips_ejtag_fdc_tty *priv = (void *)opaque;
+
+ mips_ejtag_fdc_handle(priv);
+ if (!priv->removing)
+ mod_timer_pinned(&priv->poll_timer, jiffies + FDC_TTY_POLL);
+}
+
+/* TTY Port operations */
+
+static int mips_ejtag_fdc_tty_port_activate(struct tty_port *port,
+ struct tty_struct *tty)
+{
+ struct mips_ejtag_fdc_tty_port *dport =
+ container_of(port, struct mips_ejtag_fdc_tty_port, port);
+ void *rx_buf;
+
+ /* Allocate the buffer we use for writing data */
+ if (tty_port_alloc_xmit_buf(port) < 0)
+ goto err;
+
+ /* Allocate the buffer we use for reading data */
+ rx_buf = kzalloc(RX_BUF_SIZE, GFP_KERNEL);
+ if (!rx_buf)
+ goto err_free_xmit;
+
+ raw_spin_lock_irq(&dport->rx_lock);
+ dport->rx_buf = rx_buf;
+ raw_spin_unlock_irq(&dport->rx_lock);
+
+ return 0;
+err_free_xmit:
+ tty_port_free_xmit_buf(port);
+err:
+ return -ENOMEM;
+}
+
+static void mips_ejtag_fdc_tty_port_shutdown(struct tty_port *port)
+{
+ struct mips_ejtag_fdc_tty_port *dport =
+ container_of(port, struct mips_ejtag_fdc_tty_port, port);
+ struct mips_ejtag_fdc_tty *priv = dport->driver;
+ void *rx_buf;
+ unsigned int count;
+
+ spin_lock(&dport->xmit_lock);
+ count = dport->xmit_cnt;
+ spin_unlock(&dport->xmit_lock);
+ if (count) {
+ /*
+ * There's still data to write out, so wake and wait for the
+ * writer thread to drain the buffer.
+ */
+ wake_up_interruptible(&priv->waitqueue);
+ wait_for_completion(&dport->xmit_empty);
+ }
+
+ /* Null the read buffer (timer could still be running!) */
+ raw_spin_lock_irq(&dport->rx_lock);
+ rx_buf = dport->rx_buf;
+ dport->rx_buf = NULL;
+ raw_spin_unlock_irq(&dport->rx_lock);
+ /* Free the read buffer */
+ kfree(rx_buf);
+
+ /* Free the write buffer */
+ tty_port_free_xmit_buf(port);
+}
+
+static const struct tty_port_operations mips_ejtag_fdc_tty_port_ops = {
+ .activate = mips_ejtag_fdc_tty_port_activate,
+ .shutdown = mips_ejtag_fdc_tty_port_shutdown,
+};
+
+/* TTY operations */
+
+static int mips_ejtag_fdc_tty_install(struct tty_driver *driver,
+ struct tty_struct *tty)
+{
+ struct mips_ejtag_fdc_tty *priv = driver->driver_state;
+
+ tty->driver_data = &priv->ports[tty->index];
+ return tty_port_install(&priv->ports[tty->index].port, driver, tty);
+}
+
+static int mips_ejtag_fdc_tty_open(struct tty_struct *tty, struct file *filp)
+{
+ return tty_port_open(tty->port, tty, filp);
+}
+
+static void mips_ejtag_fdc_tty_close(struct tty_struct *tty, struct file *filp)
+{
+ return tty_port_close(tty->port, tty, filp);
+}
+
+static void mips_ejtag_fdc_tty_hangup(struct tty_struct *tty)
+{
+ struct mips_ejtag_fdc_tty_port *dport = tty->driver_data;
+ struct mips_ejtag_fdc_tty *priv = dport->driver;
+
+ /* Drop any data in the xmit buffer */
+ spin_lock(&dport->xmit_lock);
+ if (dport->xmit_cnt) {
+ atomic_sub(dport->xmit_cnt, &priv->xmit_total);
+ dport->xmit_cnt = 0;
+ dport->xmit_head = 0;
+ dport->xmit_tail = 0;
+ complete(&dport->xmit_empty);
+ }
+ spin_unlock(&dport->xmit_lock);
+
+ tty_port_hangup(tty->port);
+}
+
+static int mips_ejtag_fdc_tty_write(struct tty_struct *tty,
+ const unsigned char *buf, int total)
+{
+ int count, block;
+ struct mips_ejtag_fdc_tty_port *dport = tty->driver_data;
+ struct mips_ejtag_fdc_tty *priv = dport->driver;
+
+ /*
+ * Write to output buffer.
+ *
+ * The reason that we asynchronously write the buffer is because if we
+ * were to write the buffer synchronously then because the channels are
+ * per-CPU the buffer would be written to the channel of whatever CPU
+ * we're running on.
+ *
+ * What we actually want to happen is have all input and output done on
+ * one CPU.
+ */
+ spin_lock(&dport->xmit_lock);
+ /* Work out how many bytes we can write to the xmit buffer */
+ total = min(total, (int)(priv->xmit_size - dport->xmit_cnt));
+ atomic_add(total, &priv->xmit_total);
+ dport->xmit_cnt += total;
+ /* Write the actual bytes (may need splitting if it wraps) */
+ for (count = total; count; count -= block) {
+ block = min(count, (int)(priv->xmit_size - dport->xmit_head));
+ memcpy(dport->port.xmit_buf + dport->xmit_head, buf, block);
+ dport->xmit_head += block;
+ if (dport->xmit_head >= priv->xmit_size)
+ dport->xmit_head -= priv->xmit_size;
+ buf += block;
+ }
+ count = dport->xmit_cnt;
+ /* Xmit buffer no longer empty? */
+ if (count)
+ reinit_completion(&dport->xmit_empty);
+ spin_unlock(&dport->xmit_lock);
+
+ /* Wake up the kthread */
+ if (total)
+ wake_up_interruptible(&priv->waitqueue);
+ return total;
+}
+
+static int mips_ejtag_fdc_tty_write_room(struct tty_struct *tty)
+{
+ struct mips_ejtag_fdc_tty_port *dport = tty->driver_data;
+ struct mips_ejtag_fdc_tty *priv = dport->driver;
+ int room;
+
+ /* Report the space in the xmit buffer */
+ spin_lock(&dport->xmit_lock);
+ room = priv->xmit_size - dport->xmit_cnt;
+ spin_unlock(&dport->xmit_lock);
+
+ return room;
+}
+
+static int mips_ejtag_fdc_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ struct mips_ejtag_fdc_tty_port *dport = tty->driver_data;
+ int chars;
+
+ /* Report the number of bytes in the xmit buffer */
+ spin_lock(&dport->xmit_lock);
+ chars = dport->xmit_cnt;
+ spin_unlock(&dport->xmit_lock);
+
+ return chars;
+}
+
+static const struct tty_operations mips_ejtag_fdc_tty_ops = {
+ .install = mips_ejtag_fdc_tty_install,
+ .open = mips_ejtag_fdc_tty_open,
+ .close = mips_ejtag_fdc_tty_close,
+ .hangup = mips_ejtag_fdc_tty_hangup,
+ .write = mips_ejtag_fdc_tty_write,
+ .write_room = mips_ejtag_fdc_tty_write_room,
+ .chars_in_buffer = mips_ejtag_fdc_tty_chars_in_buffer,
+};
+
+static int mips_ejtag_fdc_tty_probe(struct mips_cdmm_device *dev)
+{
+ int ret, nport;
+ struct mips_ejtag_fdc_tty_port *dport;
+ struct mips_ejtag_fdc_tty *priv;
+ struct tty_driver *driver;
+ unsigned int cfg, tx_fifo;
+
+ priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ priv->cpu = dev->cpu;
+ priv->dev = &dev->dev;
+ mips_cdmm_set_drvdata(dev, priv);
+ atomic_set(&priv->xmit_total, 0);
+ raw_spin_lock_init(&priv->lock);
+
+ priv->reg = devm_ioremap_nocache(priv->dev, dev->res.start,
+ resource_size(&dev->res));
+ if (!priv->reg) {
+ dev_err(priv->dev, "ioremap failed for resource %pR\n",
+ &dev->res);
+ return -ENOMEM;
+ }
+
+ cfg = mips_ejtag_fdc_read(priv, REG_FDCFG);
+ tx_fifo = (cfg & REG_FDCFG_TXFIFOSIZE) >> REG_FDCFG_TXFIFOSIZE_SHIFT;
+ /* Disable interrupts */
+ cfg &= ~(REG_FDCFG_TXINTTHRES | REG_FDCFG_RXINTTHRES);
+ cfg |= REG_FDCFG_TXINTTHRES_DISABLED;
+ cfg |= REG_FDCFG_RXINTTHRES_DISABLED;
+ mips_ejtag_fdc_write(priv, REG_FDCFG, cfg);
+
+ /* Make each port's xmit FIFO big enough to fill FDC TX FIFO */
+ priv->xmit_size = min(tx_fifo * 4, (unsigned int)SERIAL_XMIT_SIZE);
+
+ driver = tty_alloc_driver(NUM_TTY_CHANNELS, TTY_DRIVER_REAL_RAW);
+ if (IS_ERR(driver))
+ return PTR_ERR(driver);
+ priv->driver = driver;
+
+ driver->driver_name = "ejtag_fdc";
+ snprintf(priv->fdc_name, sizeof(priv->fdc_name), "ttyFDC%u", dev->cpu);
+ snprintf(priv->driver_name, sizeof(priv->driver_name), "%sc",
+ priv->fdc_name);
+ driver->name = priv->driver_name;
+ driver->major = 0; /* Auto-allocate */
+ driver->minor_start = 0;
+ driver->type = TTY_DRIVER_TYPE_SERIAL;
+ driver->subtype = SERIAL_TYPE_NORMAL;
+ driver->init_termios = tty_std_termios;
+ driver->init_termios.c_cflag |= CLOCAL;
+ driver->driver_state = priv;
+
+ tty_set_operations(driver, &mips_ejtag_fdc_tty_ops);
+ for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) {
+ dport = &priv->ports[nport];
+ dport->driver = priv;
+ tty_port_init(&dport->port);
+ dport->port.ops = &mips_ejtag_fdc_tty_port_ops;
+ raw_spin_lock_init(&dport->rx_lock);
+ spin_lock_init(&dport->xmit_lock);
+ /* The xmit buffer starts empty, i.e. completely written */
+ init_completion(&dport->xmit_empty);
+ complete(&dport->xmit_empty);
+ }
+
+ /* Set up the console */
+ mips_ejtag_fdc_con.regs[dev->cpu] = priv->reg;
+ if (dev->cpu == 0)
+ mips_ejtag_fdc_con.tty_drv = driver;
+
+ init_waitqueue_head(&priv->waitqueue);
+ priv->thread = kthread_create(mips_ejtag_fdc_put, priv, priv->fdc_name);
+ if (IS_ERR(priv->thread)) {
+ ret = PTR_ERR(priv->thread);
+ dev_err(priv->dev, "Couldn't create kthread (%d)\n", ret);
+ goto err_destroy_ports;
+ }
+ /*
+ * Bind the writer thread to the right CPU so it can't migrate.
+ * The channels are per-CPU and we want all channel I/O to be on a
+ * single predictable CPU.
+ */
+ kthread_bind(priv->thread, dev->cpu);
+ wake_up_process(priv->thread);
+
+ /* Look for an FDC IRQ */
+ priv->irq = -1;
+ if (get_c0_fdc_int)
+ priv->irq = get_c0_fdc_int();
+
+ /* Try requesting the IRQ */
+ if (priv->irq >= 0) {
+ /*
+ * IRQF_SHARED, IRQF_NO_SUSPEND: The FDC IRQ may be shared with
+ * other local interrupts such as the timer which sets
+ * IRQF_TIMER (including IRQF_NO_SUSPEND).
+ *
+ * IRQF_NO_THREAD: The FDC IRQ isn't individually maskable so it
+ * cannot be deferred and handled by a thread on RT kernels. For
+ * this reason any spinlocks used from the ISR are raw.
+ */
+ ret = devm_request_irq(priv->dev, priv->irq, mips_ejtag_fdc_isr,
+ IRQF_PERCPU | IRQF_SHARED |
+ IRQF_NO_THREAD | IRQF_NO_SUSPEND,
+ priv->fdc_name, priv);
+ if (ret)
+ priv->irq = -1;
+ }
+ if (priv->irq >= 0) {
+ /* IRQ is usable, enable RX interrupt */
+ raw_spin_lock_irq(&priv->lock);
+ cfg = mips_ejtag_fdc_read(priv, REG_FDCFG);
+ cfg &= ~REG_FDCFG_RXINTTHRES;
+ cfg |= REG_FDCFG_RXINTTHRES_NOTEMPTY;
+ mips_ejtag_fdc_write(priv, REG_FDCFG, cfg);
+ raw_spin_unlock_irq(&priv->lock);
+ } else {
+ /* If we didn't get an usable IRQ, poll instead */
+ setup_timer(&priv->poll_timer, mips_ejtag_fdc_tty_timer,
+ (unsigned long)priv);
+ priv->poll_timer.expires = jiffies + FDC_TTY_POLL;
+ /*
+ * Always attach the timer to the right CPU. The channels are
+ * per-CPU so all polling should be from a single CPU.
+ */
+ add_timer_on(&priv->poll_timer, dev->cpu);
+
+ dev_info(priv->dev, "No usable IRQ, polling enabled\n");
+ }
+
+ ret = tty_register_driver(driver);
+ if (ret < 0) {
+ dev_err(priv->dev, "Couldn't install tty driver (%d)\n", ret);
+ goto err_stop_irq;
+ }
+
+ return 0;
+
+err_stop_irq:
+ if (priv->irq >= 0) {
+ raw_spin_lock_irq(&priv->lock);
+ cfg = mips_ejtag_fdc_read(priv, REG_FDCFG);
+ /* Disable interrupts */
+ cfg &= ~(REG_FDCFG_TXINTTHRES | REG_FDCFG_RXINTTHRES);
+ cfg |= REG_FDCFG_TXINTTHRES_DISABLED;
+ cfg |= REG_FDCFG_RXINTTHRES_DISABLED;
+ mips_ejtag_fdc_write(priv, REG_FDCFG, cfg);
+ raw_spin_unlock_irq(&priv->lock);
+ } else {
+ priv->removing = true;
+ del_timer_sync(&priv->poll_timer);
+ }
+ kthread_stop(priv->thread);
+err_destroy_ports:
+ if (dev->cpu == 0)
+ mips_ejtag_fdc_con.tty_drv = NULL;
+ for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) {
+ dport = &priv->ports[nport];
+ tty_port_destroy(&dport->port);
+ }
+ put_tty_driver(priv->driver);
+ return ret;
+}
+
+static int mips_ejtag_fdc_tty_remove(struct mips_cdmm_device *dev)
+{
+ struct mips_ejtag_fdc_tty *priv = mips_cdmm_get_drvdata(dev);
+ struct mips_ejtag_fdc_tty_port *dport;
+ int nport;
+ unsigned int cfg;
+
+ if (priv->irq >= 0) {
+ raw_spin_lock_irq(&priv->lock);
+ cfg = mips_ejtag_fdc_read(priv, REG_FDCFG);
+ /* Disable interrupts */
+ cfg &= ~(REG_FDCFG_TXINTTHRES | REG_FDCFG_RXINTTHRES);
+ cfg |= REG_FDCFG_TXINTTHRES_DISABLED;
+ cfg |= REG_FDCFG_RXINTTHRES_DISABLED;
+ mips_ejtag_fdc_write(priv, REG_FDCFG, cfg);
+ raw_spin_unlock_irq(&priv->lock);
+ } else {
+ priv->removing = true;
+ del_timer_sync(&priv->poll_timer);
+ }
+ kthread_stop(priv->thread);
+ if (dev->cpu == 0)
+ mips_ejtag_fdc_con.tty_drv = NULL;
+ tty_unregister_driver(priv->driver);
+ for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) {
+ dport = &priv->ports[nport];
+ tty_port_destroy(&dport->port);
+ }
+ put_tty_driver(priv->driver);
+ return 0;
+}
+
+static int mips_ejtag_fdc_tty_cpu_down(struct mips_cdmm_device *dev)
+{
+ struct mips_ejtag_fdc_tty *priv = mips_cdmm_get_drvdata(dev);
+ unsigned int cfg;
+
+ if (priv->irq >= 0) {
+ raw_spin_lock_irq(&priv->lock);
+ cfg = mips_ejtag_fdc_read(priv, REG_FDCFG);
+ /* Disable interrupts */
+ cfg &= ~(REG_FDCFG_TXINTTHRES | REG_FDCFG_RXINTTHRES);
+ cfg |= REG_FDCFG_TXINTTHRES_DISABLED;
+ cfg |= REG_FDCFG_RXINTTHRES_DISABLED;
+ mips_ejtag_fdc_write(priv, REG_FDCFG, cfg);
+ raw_spin_unlock_irq(&priv->lock);
+ } else {
+ priv->removing = true;
+ del_timer_sync(&priv->poll_timer);
+ }
+ kthread_stop(priv->thread);
+
+ return 0;
+}
+
+static int mips_ejtag_fdc_tty_cpu_up(struct mips_cdmm_device *dev)
+{
+ struct mips_ejtag_fdc_tty *priv = mips_cdmm_get_drvdata(dev);
+ unsigned int cfg;
+ int ret = 0;
+
+ if (priv->irq >= 0) {
+ /*
+ * IRQ is usable, enable RX interrupt
+ * This must be before kthread is restarted, as kthread may
+ * enable TX interrupt.
+ */
+ raw_spin_lock_irq(&priv->lock);
+ cfg = mips_ejtag_fdc_read(priv, REG_FDCFG);
+ cfg &= ~(REG_FDCFG_TXINTTHRES | REG_FDCFG_RXINTTHRES);
+ cfg |= REG_FDCFG_TXINTTHRES_DISABLED;
+ cfg |= REG_FDCFG_RXINTTHRES_NOTEMPTY;
+ mips_ejtag_fdc_write(priv, REG_FDCFG, cfg);
+ raw_spin_unlock_irq(&priv->lock);
+ } else {
+ /* Restart poll timer */
+ priv->removing = false;
+ add_timer_on(&priv->poll_timer, dev->cpu);
+ }
+
+ /* Restart the kthread */
+ priv->thread = kthread_create(mips_ejtag_fdc_put, priv, priv->fdc_name);
+ if (IS_ERR(priv->thread)) {
+ ret = PTR_ERR(priv->thread);
+ dev_err(priv->dev, "Couldn't re-create kthread (%d)\n", ret);
+ goto out;
+ }
+ /* Bind it back to the right CPU and set it off */
+ kthread_bind(priv->thread, dev->cpu);
+ wake_up_process(priv->thread);
+out:
+ return ret;
+}
+
+static struct mips_cdmm_device_id mips_ejtag_fdc_tty_ids[] = {
+ { .type = 0xfd },
+ { }
+};
+
+static struct mips_cdmm_driver mips_ejtag_fdc_tty_driver = {
+ .drv = {
+ .name = "mips_ejtag_fdc",
+ },
+ .probe = mips_ejtag_fdc_tty_probe,
+ .remove = mips_ejtag_fdc_tty_remove,
+ .cpu_down = mips_ejtag_fdc_tty_cpu_down,
+ .cpu_up = mips_ejtag_fdc_tty_cpu_up,
+ .id_table = mips_ejtag_fdc_tty_ids,
+};
+module_mips_cdmm_driver(mips_ejtag_fdc_tty_driver);
+
+static int __init mips_ejtag_fdc_init_console(void)
+{
+ return mips_ejtag_fdc_console_init(&mips_ejtag_fdc_con);
+}
+console_initcall(mips_ejtag_fdc_init_console);
+
+#ifdef CONFIG_MIPS_EJTAG_FDC_EARLYCON
+static struct mips_ejtag_fdc_console mips_ejtag_fdc_earlycon = {
+ .cons = {
+ .name = "early_fdc",
+ .write = mips_ejtag_fdc_console_write,
+ .flags = CON_PRINTBUFFER | CON_BOOT,
+ .index = CONSOLE_CHANNEL,
+ },
+ .lock = __RAW_SPIN_LOCK_UNLOCKED(mips_ejtag_fdc_earlycon.lock),
+};
+
+int __init setup_early_fdc_console(void)
+{
+ return mips_ejtag_fdc_console_init(&mips_ejtag_fdc_earlycon);
+}
+#endif
+
+#ifdef CONFIG_MIPS_EJTAG_FDC_KGDB
+
+/* read buffer to allow decompaction */
+static unsigned int kgdbfdc_rbuflen;
+static unsigned int kgdbfdc_rpos;
+static char kgdbfdc_rbuf[4];
+
+/* write buffer to allow compaction */
+static unsigned int kgdbfdc_wbuflen;
+static char kgdbfdc_wbuf[4];
+
+static void __iomem *kgdbfdc_setup(void)
+{
+ void __iomem *regs;
+ unsigned int cpu;
+
+ /* Find address, piggy backing off console percpu regs */
+ cpu = smp_processor_id();
+ regs = mips_ejtag_fdc_con.regs[cpu];
+ /* First console output on this CPU? */
+ if (!regs) {
+ regs = mips_cdmm_early_probe(0xfd);
+ mips_ejtag_fdc_con.regs[cpu] = regs;
+ }
+ /* Already tried and failed to find FDC on this CPU? */
+ if (IS_ERR(regs))
+ return regs;
+
+ return regs;
+}
+
+/* read a character from the read buffer, filling from FDC RX FIFO */
+static int kgdbfdc_read_char(void)
+{
+ unsigned int stat, channel, data;
+ void __iomem *regs;
+
+ /* No more data, try and read another FDC word from RX FIFO */
+ if (kgdbfdc_rpos >= kgdbfdc_rbuflen) {
+ kgdbfdc_rpos = 0;
+ kgdbfdc_rbuflen = 0;
+
+ regs = kgdbfdc_setup();
+ if (IS_ERR(regs))
+ return NO_POLL_CHAR;
+
+ /* Read next word from KGDB channel */
+ do {
+ stat = ioread32(regs + REG_FDSTAT);
+
+ /* No data waiting? */
+ if (stat & REG_FDSTAT_RXE)
+ return NO_POLL_CHAR;
+
+ /* Read next word */
+ channel = (stat & REG_FDSTAT_RXCHAN) >>
+ REG_FDSTAT_RXCHAN_SHIFT;
+ data = ioread32(regs + REG_FDRX);
+ } while (channel != CONFIG_MIPS_EJTAG_FDC_KGDB_CHAN);
+
+ /* Decode into rbuf */
+ kgdbfdc_rbuflen = mips_ejtag_fdc_decode(data, kgdbfdc_rbuf);
+ }
+ pr_devel("kgdbfdc r %c\n", kgdbfdc_rbuf[kgdbfdc_rpos]);
+ return kgdbfdc_rbuf[kgdbfdc_rpos++];
+}
+
+/* push an FDC word from write buffer to TX FIFO */
+static void kgdbfdc_push_one(void)
+{
+ const char *bufs[1] = { kgdbfdc_wbuf };
+ struct fdc_word word;
+ void __iomem *regs;
+ unsigned int i;
+
+ /* Construct a word from any data in buffer */
+ word = mips_ejtag_fdc_encode(bufs, &kgdbfdc_wbuflen, 1);
+ /* Relocate any remaining data to beginnning of buffer */
+ kgdbfdc_wbuflen -= word.bytes;
+ for (i = 0; i < kgdbfdc_wbuflen; ++i)
+ kgdbfdc_wbuf[i] = kgdbfdc_wbuf[i + word.bytes];
+
+ regs = kgdbfdc_setup();
+ if (IS_ERR(regs))
+ return;
+
+ /* Busy wait until there's space in fifo */
+ while (ioread32(regs + REG_FDSTAT) & REG_FDSTAT_TXF)
+ ;
+ iowrite32(word.word, regs + REG_FDTX(CONFIG_MIPS_EJTAG_FDC_KGDB_CHAN));
+}
+
+/* flush the whole write buffer to the TX FIFO */
+static void kgdbfdc_flush(void)
+{
+ while (kgdbfdc_wbuflen)
+ kgdbfdc_push_one();
+}
+
+/* write a character into the write buffer, writing out if full */
+static void kgdbfdc_write_char(u8 chr)
+{
+ pr_devel("kgdbfdc w %c\n", chr);
+ kgdbfdc_wbuf[kgdbfdc_wbuflen++] = chr;
+ if (kgdbfdc_wbuflen >= sizeof(kgdbfdc_wbuf))
+ kgdbfdc_push_one();
+}
+
+static struct kgdb_io kgdbfdc_io_ops = {
+ .name = "kgdbfdc",
+ .read_char = kgdbfdc_read_char,
+ .write_char = kgdbfdc_write_char,
+ .flush = kgdbfdc_flush,
+};
+
+static int __init kgdbfdc_init(void)
+{
+ kgdb_register_io_module(&kgdbfdc_io_ops);
+ return 0;
+}
+early_initcall(kgdbfdc_init);
+#endif