aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/acpica/aclocal.h1
-rw-r--r--drivers/acpi/acpica/acnamesp.h17
-rw-r--r--drivers/acpi/acpica/acutils.h2
-rw-r--r--drivers/acpi/acpica/dbinput.c10
-rw-r--r--drivers/acpi/acpica/dbmethod.c8
-rw-r--r--drivers/acpi/acpica/dbxface.c10
-rw-r--r--drivers/acpi/acpica/dsfield.c34
-rw-r--r--drivers/acpi/acpica/hwregs.c9
-rw-r--r--drivers/acpi/acpica/hwsleep.c11
-rw-r--r--drivers/acpi/acpica/nsaccess.c13
-rw-r--r--drivers/acpi/acpica/psloop.c43
-rw-r--r--drivers/acpi/acpica/tbdata.c4
-rw-r--r--drivers/acpi/acpica/utdelete.c7
-rw-r--r--drivers/acpi/acpica/utstrsuppt.c26
-rw-r--r--drivers/acpi/acpica/utstrtoul64.c2
-rw-r--r--drivers/acpi/pmic/intel_pmic_crc.c109
-rw-r--r--drivers/cpufreq/cpufreq_governor.c12
-rw-r--r--drivers/cpuidle/governors/menu.c45
-rw-r--r--drivers/i2c/busses/Kconfig42
-rw-r--r--drivers/i2c/busses/Makefile3
-rw-r--r--drivers/i2c/busses/i2c-amd8111.c1
-rw-r--r--drivers/i2c/busses/i2c-aspeed.c40
-rw-r--r--drivers/i2c/busses/i2c-brcmstb.c8
-rw-r--r--drivers/i2c/busses/i2c-davinci.c4
-rw-r--r--drivers/i2c/busses/i2c-designware-baytrail.c10
-rw-r--r--drivers/i2c/busses/i2c-designware-common.c91
-rw-r--r--drivers/i2c/busses/i2c-designware-core.h24
-rw-r--r--drivers/i2c/busses/i2c-designware-master.c223
-rw-r--r--drivers/i2c/busses/i2c-designware-pcidrv.c17
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c68
-rw-r--r--drivers/i2c/busses/i2c-designware-slave.c62
-rw-r--r--drivers/i2c/busses/i2c-exynos5.c22
-rw-r--r--drivers/i2c/busses/i2c-fsi.c752
-rw-r--r--drivers/i2c/busses/i2c-gpio.c97
-rw-r--r--drivers/i2c/busses/i2c-i801.c4
-rw-r--r--drivers/i2c/busses/i2c-imx.c23
-rw-r--r--drivers/i2c/busses/i2c-mxs.c8
-rw-r--r--drivers/i2c/busses/i2c-owl.c495
-rw-r--r--drivers/i2c/busses/i2c-pasemi.c3
-rw-r--r--drivers/i2c/busses/i2c-pmcmsp.c17
-rw-r--r--drivers/i2c/busses/i2c-qcom-geni.c673
-rw-r--r--drivers/i2c/busses/i2c-rcar.c73
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c4
-rw-r--r--drivers/i2c/busses/i2c-sh_mobile.c10
-rw-r--r--drivers/i2c/busses/i2c-sprd.c8
-rw-r--r--drivers/i2c/busses/i2c-stu300.c12
-rw-r--r--drivers/i2c/busses/i2c-tegra.c90
-rw-r--r--drivers/i2c/busses/i2c-xlr.c11
-rw-r--r--drivers/i2c/i2c-core-acpi.c19
-rw-r--r--drivers/i2c/i2c-core-base.c85
-rw-r--r--drivers/i2c/i2c-core-slave.c8
-rw-r--r--drivers/i2c/i2c-mux.c4
-rw-r--r--drivers/i2c/muxes/i2c-mux-mlxcpld.c28
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca9541.c75
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca954x.c51
-rw-r--r--drivers/ide/hpt366.c5
-rw-r--r--drivers/ide/ide-floppy.c1
-rw-r--r--drivers/ide/ide-io.c4
-rw-r--r--drivers/ide/ide-probe.c1
-rw-r--r--drivers/ide/ide-tape.c2
-rw-r--r--drivers/ide/ide-taskfile.c3
-rw-r--r--drivers/ide/sis5513.c1
-rw-r--r--drivers/iio/temperature/mlx90614.c4
-rw-r--r--drivers/input/touchscreen/rohm_bu21023.c4
-rw-r--r--drivers/irqchip/irq-renesas-h8s.c6
-rw-r--r--drivers/media/dvb-frontends/af9013.c8
-rw-r--r--drivers/media/dvb-frontends/drxk_hard.c4
-rw-r--r--drivers/media/dvb-frontends/rtl2830.c12
-rw-r--r--drivers/media/dvb-frontends/tda1004x.c6
-rw-r--r--drivers/media/tuners/tda18271-common.c8
-rw-r--r--drivers/mfd/88pm860x-i2c.c8
-rw-r--r--drivers/misc/eeprom/at24.c17
-rw-r--r--drivers/net/tun.c2
-rw-r--r--drivers/pci/pci-acpi.c6
-rw-r--r--drivers/platform/chrome/chromeos_tbmc.c10
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c1
-rw-r--r--drivers/power/reset/Kconfig11
-rw-r--r--drivers/power/reset/Makefile1
-rw-r--r--drivers/power/reset/gemini-poweroff.c12
-rw-r--r--drivers/power/reset/qcom-pon.c91
-rw-r--r--drivers/power/reset/vexpress-poweroff.c12
-rw-r--r--drivers/power/reset/zx-reboot.c1
-rw-r--r--drivers/power/supply/Kconfig23
-rw-r--r--drivers/power/supply/Makefile2
-rw-r--r--drivers/power/supply/ab8500_fg.c14
-rw-r--r--drivers/power/supply/adp5061.c745
-rw-r--r--drivers/power/supply/axp20x_usb_power.c1
-rw-r--r--drivers/power/supply/axp288_charger.c2
-rw-r--r--drivers/power/supply/bq27xxx_battery.c3
-rw-r--r--drivers/power/supply/cros_usbpd-charger.c545
-rw-r--r--drivers/power/supply/ds2760_battery.c348
-rw-r--r--drivers/power/supply/generic-adc-battery.c25
-rw-r--r--drivers/power/supply/lego_ev3_battery.c20
-rw-r--r--drivers/power/supply/max1721x_battery.c2
-rw-r--r--drivers/power/supply/max77693_charger.c1
-rw-r--r--drivers/power/supply/power_supply_core.c11
-rw-r--r--drivers/power/supply/sbs-battery.c67
-rw-r--r--drivers/power/supply/tps65217_charger.c22
-rw-r--r--drivers/power/supply/wm8350_power.c3
-rw-r--r--drivers/tty/sysrq.c2
-rw-r--r--drivers/tty/tty_io.c2
-rw-r--r--drivers/w1/slaves/Kconfig12
-rw-r--r--drivers/w1/slaves/Makefile1
-rw-r--r--drivers/w1/slaves/w1_ds2760.c175
-rw-r--r--drivers/w1/slaves/w1_ds2760.h59
-rw-r--r--drivers/w1/w1.c3
106 files changed, 4742 insertions, 1115 deletions
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h
index c5367bf5487f..0f28a38a43ea 100644
--- a/drivers/acpi/acpica/aclocal.h
+++ b/drivers/acpi/acpica/aclocal.h
@@ -164,6 +164,7 @@ struct acpi_namespace_node {
#define ANOBJ_SUBTREE_HAS_INI 0x10 /* Used to optimize device initialization */
#define ANOBJ_EVALUATED 0x20 /* Set on first evaluation of node */
#define ANOBJ_ALLOCATED_BUFFER 0x40 /* Method AML buffer is dynamic (install_method) */
+#define ANOBJ_NODE_EARLY_INIT 0x80 /* acpi_exec only: Node was create via init file (-fi) */
#define ANOBJ_IS_EXTERNAL 0x08 /* iASL only: This object created via External() */
#define ANOBJ_METHOD_NO_RETVAL 0x10 /* iASL only: Method has no return value */
diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h
index 3825df923480..bbb3b4d1e796 100644
--- a/drivers/acpi/acpica/acnamesp.h
+++ b/drivers/acpi/acpica/acnamesp.h
@@ -25,14 +25,15 @@
/* Flags for acpi_ns_lookup, acpi_ns_search_and_enter */
#define ACPI_NS_NO_UPSEARCH 0
-#define ACPI_NS_SEARCH_PARENT 0x01
-#define ACPI_NS_DONT_OPEN_SCOPE 0x02
-#define ACPI_NS_NO_PEER_SEARCH 0x04
-#define ACPI_NS_ERROR_IF_FOUND 0x08
-#define ACPI_NS_PREFIX_IS_SCOPE 0x10
-#define ACPI_NS_EXTERNAL 0x20
-#define ACPI_NS_TEMPORARY 0x40
-#define ACPI_NS_OVERRIDE_IF_FOUND 0x80
+#define ACPI_NS_SEARCH_PARENT 0x0001
+#define ACPI_NS_DONT_OPEN_SCOPE 0x0002
+#define ACPI_NS_NO_PEER_SEARCH 0x0004
+#define ACPI_NS_ERROR_IF_FOUND 0x0008
+#define ACPI_NS_PREFIX_IS_SCOPE 0x0010
+#define ACPI_NS_EXTERNAL 0x0020
+#define ACPI_NS_TEMPORARY 0x0040
+#define ACPI_NS_OVERRIDE_IF_FOUND 0x0080
+#define ACPI_NS_EARLY_INIT 0x0100
/* Flags for acpi_ns_walk_namespace */
diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h
index 2733cd4e418c..3374d41582b5 100644
--- a/drivers/acpi/acpica/acutils.h
+++ b/drivers/acpi/acpica/acutils.h
@@ -180,6 +180,8 @@ char acpi_ut_remove_leading_zeros(char **string);
u8 acpi_ut_detect_hex_prefix(char **string);
+void acpi_ut_remove_hex_prefix(char **string);
+
u8 acpi_ut_detect_octal_prefix(char **string);
/*
diff --git a/drivers/acpi/acpica/dbinput.c b/drivers/acpi/acpica/dbinput.c
index 556ff59bbbfc..3e5f95390f0d 100644
--- a/drivers/acpi/acpica/dbinput.c
+++ b/drivers/acpi/acpica/dbinput.c
@@ -763,7 +763,12 @@ acpi_db_command_dispatch(char *input_buffer,
case CMD_DISASSEMBLE:
case CMD_DISASM:
+#ifdef ACPI_DISASSEMBLER
(void)acpi_db_disassemble_method(acpi_gbl_db_args[1]);
+#else
+ acpi_os_printf
+ ("The AML Disassembler is not configured/present\n");
+#endif
break;
case CMD_DUMP:
@@ -872,7 +877,12 @@ acpi_db_command_dispatch(char *input_buffer,
case CMD_LIST:
+#ifdef ACPI_DISASSEMBLER
acpi_db_disassemble_aml(acpi_gbl_db_args[1], op);
+#else
+ acpi_os_printf
+ ("The AML Disassembler is not configured/present\n");
+#endif
break;
case CMD_LOCKS:
diff --git a/drivers/acpi/acpica/dbmethod.c b/drivers/acpi/acpica/dbmethod.c
index 9fcecf104ba0..d8b7a0fe92ec 100644
--- a/drivers/acpi/acpica/dbmethod.c
+++ b/drivers/acpi/acpica/dbmethod.c
@@ -216,6 +216,7 @@ cleanup:
acpi_ut_remove_reference(obj_desc);
}
+#ifdef ACPI_DISASSEMBLER
/*******************************************************************************
*
* FUNCTION: acpi_db_disassemble_aml
@@ -242,9 +243,8 @@ void acpi_db_disassemble_aml(char *statements, union acpi_parse_object *op)
if (statements) {
num_statements = strtoul(statements, NULL, 0);
}
-#ifdef ACPI_DISASSEMBLER
+
acpi_dm_disassemble(NULL, op, num_statements);
-#endif
}
/*******************************************************************************
@@ -317,8 +317,6 @@ acpi_status acpi_db_disassemble_method(char *name)
walk_state->parse_flags |= ACPI_PARSE_DISASSEMBLE;
status = acpi_ps_parse_aml(walk_state);
-
-#ifdef ACPI_DISASSEMBLER
(void)acpi_dm_parse_deferred_ops(op);
/* Now we can disassemble the method */
@@ -326,7 +324,6 @@ acpi_status acpi_db_disassemble_method(char *name)
acpi_gbl_dm_opt_verbose = FALSE;
acpi_dm_disassemble(NULL, op, 0);
acpi_gbl_dm_opt_verbose = TRUE;
-#endif
acpi_ps_delete_parse_tree(op);
@@ -337,6 +334,7 @@ acpi_status acpi_db_disassemble_method(char *name)
acpi_ut_release_owner_id(&obj_desc->method.owner_id);
return (AE_OK);
}
+#endif
/*******************************************************************************
*
diff --git a/drivers/acpi/acpica/dbxface.c b/drivers/acpi/acpica/dbxface.c
index 4647aa8efecb..f2526726daf6 100644
--- a/drivers/acpi/acpica/dbxface.c
+++ b/drivers/acpi/acpica/dbxface.c
@@ -10,6 +10,7 @@
#include "amlcode.h"
#include "acdebug.h"
#include "acinterp.h"
+#include "acparser.h"
#define _COMPONENT ACPI_CA_DEBUGGER
ACPI_MODULE_NAME("dbxface")
@@ -262,10 +263,17 @@ acpi_db_single_step(struct acpi_walk_state *walk_state,
}
}
- /* Now we can display it */
+ /* Now we can disassemble and display it */
#ifdef ACPI_DISASSEMBLER
acpi_dm_disassemble(walk_state, display_op, ACPI_UINT32_MAX);
+#else
+ /*
+ * The AML Disassembler is not configured - at least we can
+ * display the opcode value and name
+ */
+ acpi_os_printf("AML Opcode: %4.4X %s\n", op->common.aml_opcode,
+ acpi_ps_get_opcode_name(op->common.aml_opcode));
#endif
if ((op->common.aml_opcode == AML_IF_OP) ||
diff --git a/drivers/acpi/acpica/dsfield.c b/drivers/acpi/acpica/dsfield.c
index 7c937595dfcb..30fe89545d6a 100644
--- a/drivers/acpi/acpica/dsfield.c
+++ b/drivers/acpi/acpica/dsfield.c
@@ -15,6 +15,10 @@
#include "acnamesp.h"
#include "acparser.h"
+#ifdef ACPI_EXEC_APP
+#include "aecommon.h"
+#endif
+
#define _COMPONENT ACPI_DISPATCHER
ACPI_MODULE_NAME("dsfield")
@@ -259,6 +263,13 @@ acpi_ds_get_field_names(struct acpi_create_field_info *info,
u64 position;
union acpi_parse_object *child;
+#ifdef ACPI_EXEC_APP
+ u64 value = 0;
+ union acpi_operand_object *result_desc;
+ union acpi_operand_object *obj_desc;
+ char *name_path;
+#endif
+
ACPI_FUNCTION_TRACE_PTR(ds_get_field_names, info);
/* First field starts at bit zero */
@@ -391,6 +402,25 @@ acpi_ds_get_field_names(struct acpi_create_field_info *info,
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
+#ifdef ACPI_EXEC_APP
+ name_path =
+ acpi_ns_get_external_pathname(info->
+ field_node);
+ obj_desc =
+ acpi_ut_create_integer_object
+ (value);
+ if (ACPI_SUCCESS
+ (ae_lookup_init_file_entry
+ (name_path, &value))) {
+ acpi_ex_write_data_to_field
+ (obj_desc,
+ acpi_ns_get_attached_object
+ (info->field_node),
+ &result_desc);
+ }
+ acpi_ut_remove_reference(obj_desc);
+ ACPI_FREE(name_path);
+#endif
}
}
@@ -573,7 +603,9 @@ acpi_ds_init_field_objects(union acpi_parse_object *op,
!(walk_state->parse_flags & ACPI_PARSE_MODULE_LEVEL)) {
flags |= ACPI_NS_TEMPORARY;
}
-
+#ifdef ACPI_EXEC_APP
+ flags |= ACPI_NS_OVERRIDE_IF_FOUND;
+#endif
/*
* Walk the list of entries in the field_list
* Note: field_list can be of zero length. In this case, Arg will be NULL.
diff --git a/drivers/acpi/acpica/hwregs.c b/drivers/acpi/acpica/hwregs.c
index 3de794bcf8fa..69603ba52a3a 100644
--- a/drivers/acpi/acpica/hwregs.c
+++ b/drivers/acpi/acpica/hwregs.c
@@ -528,13 +528,18 @@ acpi_status acpi_hw_register_read(u32 register_id, u32 *return_value)
status =
acpi_hw_read(&value64, &acpi_gbl_FADT.xpm2_control_block);
- value = (u32)value64;
+ if (ACPI_SUCCESS(status)) {
+ value = (u32)value64;
+ }
break;
case ACPI_REGISTER_PM_TIMER: /* 32-bit access */
status = acpi_hw_read(&value64, &acpi_gbl_FADT.xpm_timer_block);
- value = (u32)value64;
+ if (ACPI_SUCCESS(status)) {
+ value = (u32)value64;
+ }
+
break;
case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */
diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c
index fe9d46d81750..d8b8fc2ff563 100644
--- a/drivers/acpi/acpica/hwsleep.c
+++ b/drivers/acpi/acpica/hwsleep.c
@@ -56,14 +56,9 @@ acpi_status acpi_hw_legacy_sleep(u8 sleep_state)
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
- /*
- * If the target sleep state is S5, clear all GPEs and fixed events too
- */
- if (sleep_state == ACPI_STATE_S5) {
- status = acpi_hw_clear_acpi_status();
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
+ status = acpi_hw_clear_acpi_status();
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
}
acpi_gbl_system_awake_and_running = FALSE;
diff --git a/drivers/acpi/acpica/nsaccess.c b/drivers/acpi/acpica/nsaccess.c
index 83a593e2155d..e3f10afde5ff 100644
--- a/drivers/acpi/acpica/nsaccess.c
+++ b/drivers/acpi/acpica/nsaccess.c
@@ -558,6 +558,14 @@ acpi_ns_lookup(union acpi_generic_state *scope_info,
(char *)&current_node->name,
current_node));
}
+#ifdef ACPI_EXEC_APP
+ if ((status == AE_ALREADY_EXISTS) &&
+ (this_node->flags & ANOBJ_NODE_EARLY_INIT)) {
+ this_node->flags &= ~ANOBJ_NODE_EARLY_INIT;
+ status = AE_OK;
+ }
+#endif
+
#ifdef ACPI_ASL_COMPILER
/*
* If this ACPI name already exists within the namespace as an
@@ -676,6 +684,11 @@ acpi_ns_lookup(union acpi_generic_state *scope_info,
}
}
}
+#ifdef ACPI_EXEC_APP
+ if (flags & ACPI_NS_EARLY_INIT) {
+ this_node->flags |= ANOBJ_NODE_EARLY_INIT;
+ }
+#endif
*return_node = this_node;
return_ACPI_STATUS(AE_OK);
diff --git a/drivers/acpi/acpica/psloop.c b/drivers/acpi/acpica/psloop.c
index 44f35ab3347d..34fc2f7476ed 100644
--- a/drivers/acpi/acpica/psloop.c
+++ b/drivers/acpi/acpica/psloop.c
@@ -22,6 +22,7 @@
#include "acdispat.h"
#include "amlcode.h"
#include "acconvert.h"
+#include "acnamesp.h"
#define _COMPONENT ACPI_PARSER
ACPI_MODULE_NAME("psloop")
@@ -527,12 +528,18 @@ acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state)
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
- if (walk_state->opcode == AML_SCOPE_OP) {
+ if (acpi_ns_opens_scope
+ (acpi_ps_get_opcode_info
+ (walk_state->opcode)->object_type)) {
/*
- * If the scope op fails to parse, skip the body of the
- * scope op because the parse failure indicates that the
- * device may not exist.
+ * If the scope/device op fails to parse, skip the body of
+ * the scope op because the parse failure indicates that
+ * the device may not exist.
*/
+ ACPI_ERROR((AE_INFO,
+ "Skip parsing opcode %s",
+ acpi_ps_get_opcode_name
+ (walk_state->opcode)));
walk_state->parser_state.aml =
walk_state->aml + 1;
walk_state->parser_state.aml =
@@ -540,8 +547,6 @@ acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state)
(&walk_state->parser_state);
walk_state->aml =
walk_state->parser_state.aml;
- ACPI_ERROR((AE_INFO,
- "Skipping Scope block"));
}
continue;
@@ -709,20 +714,20 @@ acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state)
} else
if ((walk_state->
parse_flags & ACPI_PARSE_MODULE_LEVEL)
- && status != AE_CTRL_TRANSFER
- && ACPI_FAILURE(status)) {
+ && (ACPI_AML_EXCEPTION(status)
+ || status == AE_ALREADY_EXISTS
+ || status == AE_NOT_FOUND)) {
/*
- * ACPI_PARSE_MODULE_LEVEL flag means that we are currently
- * loading a table by executing it as a control method.
- * However, if we encounter an error while loading the table,
- * we need to keep trying to load the table rather than
- * aborting the table load (setting the status to AE_OK
- * continues the table load). If we get a failure at this
- * point, it means that the dispatcher got an error while
- * processing Op (most likely an AML operand error) or a
- * control method was called from module level and the
- * dispatcher returned AE_CTRL_TRANSFER. In the latter case,
- * leave the status alone, there's nothing wrong with it.
+ * ACPI_PARSE_MODULE_LEVEL flag means that we
+ * are currently loading a table by executing
+ * it as a control method. However, if we
+ * encounter an error while loading the table,
+ * we need to keep trying to load the table
+ * rather than aborting the table load (setting
+ * the status to AE_OK continues the table
+ * load). If we get a failure at this point, it
+ * means that the dispatcher got an error while
+ * trying to execute the Op.
*/
status = AE_OK;
}
diff --git a/drivers/acpi/acpica/tbdata.c b/drivers/acpi/acpica/tbdata.c
index 51891f9fb057..862149c8a208 100644
--- a/drivers/acpi/acpica/tbdata.c
+++ b/drivers/acpi/acpica/tbdata.c
@@ -516,9 +516,9 @@ acpi_tb_verify_temp_table(struct acpi_table_desc *table_desc,
acpi_tb_check_duplication(table_desc, table_index);
if (ACPI_FAILURE(status)) {
if (status != AE_CTRL_TERMINATE) {
- ACPI_EXCEPTION((AE_INFO, AE_NO_MEMORY,
+ ACPI_EXCEPTION((AE_INFO, status,
"%4.4s 0x%8.8X%8.8X"
- " Table is duplicated",
+ " Table is already loaded",
acpi_ut_valid_nameseg
(table_desc->signature.
ascii) ? table_desc->
diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c
index 118f3ff1fbb5..8cc4392c61f3 100644
--- a/drivers/acpi/acpica/utdelete.c
+++ b/drivers/acpi/acpica/utdelete.c
@@ -355,6 +355,7 @@ acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action)
u16 original_count;
u16 new_count = 0;
acpi_cpu_flags lock_flags;
+ char *message;
ACPI_FUNCTION_NAME(ut_update_ref_count);
@@ -391,6 +392,7 @@ acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action)
object, object->common.type,
acpi_ut_get_object_type_name(object),
new_count));
+ message = "Incremement";
break;
case REF_DECREMENT:
@@ -420,6 +422,7 @@ acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action)
if (new_count == 0) {
acpi_ut_delete_internal_obj(object);
}
+ message = "Decrement";
break;
default:
@@ -436,8 +439,8 @@ acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action)
*/
if (new_count > ACPI_MAX_REFERENCE_COUNT) {
ACPI_WARNING((AE_INFO,
- "Large Reference Count (0x%X) in object %p, Type=0x%.2X",
- new_count, object, object->common.type));
+ "Large Reference Count (0x%X) in object %p, Type=0x%.2X Operation=%s",
+ new_count, object, object->common.type, message));
}
}
diff --git a/drivers/acpi/acpica/utstrsuppt.c b/drivers/acpi/acpica/utstrsuppt.c
index 954f8e3e35cd..05ff20049b87 100644
--- a/drivers/acpi/acpica/utstrsuppt.c
+++ b/drivers/acpi/acpica/utstrsuppt.c
@@ -231,14 +231,34 @@ char acpi_ut_remove_whitespace(char **string)
u8 acpi_ut_detect_hex_prefix(char **string)
{
+ char *initial_position = *string;
+ acpi_ut_remove_hex_prefix(string);
+ if (*string != initial_position) {
+ return (TRUE); /* String is past leading 0x */
+ }
+
+ return (FALSE); /* Not a hex string */
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_remove_hex_prefix
+ *
+ * PARAMETERS: string - Pointer to input ASCII string
+ *
+ * RETURN: none
+ *
+ * DESCRIPTION: Remove a hex "0x" prefix
+ *
+ ******************************************************************************/
+
+void acpi_ut_remove_hex_prefix(char **string)
+{
if ((**string == ACPI_ASCII_ZERO) &&
(tolower((int)*(*string + 1)) == 'x')) {
*string += 2; /* Go past the leading 0x */
- return (TRUE);
}
-
- return (FALSE); /* Not a hex string */
}
/*******************************************************************************
diff --git a/drivers/acpi/acpica/utstrtoul64.c b/drivers/acpi/acpica/utstrtoul64.c
index 8fadad242db6..5fde619a8bbd 100644
--- a/drivers/acpi/acpica/utstrtoul64.c
+++ b/drivers/acpi/acpica/utstrtoul64.c
@@ -218,7 +218,7 @@ u64 acpi_ut_implicit_strtoul64(char *string)
* implicit conversions, and the "0x" prefix is "not allowed".
* However, allow a "0x" prefix as an ACPI extension.
*/
- acpi_ut_detect_hex_prefix(&string);
+ acpi_ut_remove_hex_prefix(&string);
if (!acpi_ut_remove_leading_zeros(&string)) {
return_VALUE(0);
diff --git a/drivers/acpi/pmic/intel_pmic_crc.c b/drivers/acpi/pmic/intel_pmic_crc.c
index 7ffa74048107..22c9e374c923 100644
--- a/drivers/acpi/pmic/intel_pmic_crc.c
+++ b/drivers/acpi/pmic/intel_pmic_crc.c
@@ -25,16 +25,121 @@
#define PMIC_A0LOCK_REG 0xc5
static struct pmic_table power_table[] = {
+/* {
+ .address = 0x00,
+ .reg = ??,
+ .bit = ??,
+ }, ** VSYS */
+ {
+ .address = 0x04,
+ .reg = 0x63,
+ .bit = 0x00,
+ }, /* SYSX -> VSYS_SX */
+ {
+ .address = 0x08,
+ .reg = 0x62,
+ .bit = 0x00,
+ }, /* SYSU -> VSYS_U */
+ {
+ .address = 0x0c,
+ .reg = 0x64,
+ .bit = 0x00,
+ }, /* SYSS -> VSYS_S */
+ {
+ .address = 0x10,
+ .reg = 0x6a,
+ .bit = 0x00,
+ }, /* V50S -> V5P0S */
+ {
+ .address = 0x14,
+ .reg = 0x6b,
+ .bit = 0x00,
+ }, /* HOST -> VHOST, USB2/3 host */
+ {
+ .address = 0x18,
+ .reg = 0x6c,
+ .bit = 0x00,
+ }, /* VBUS -> VBUS, USB2/3 OTG */
+ {
+ .address = 0x1c,
+ .reg = 0x6d,
+ .bit = 0x00,
+ }, /* HDMI -> VHDMI */
+/* {
+ .address = 0x20,
+ .reg = ??,
+ .bit = ??,
+ }, ** S285 */
{
.address = 0x24,
.reg = 0x66,
.bit = 0x00,
- },
+ }, /* X285 -> V2P85SX, camera */
+/* {
+ .address = 0x28,
+ .reg = ??,
+ .bit = ??,
+ }, ** V33A */
+ {
+ .address = 0x2c,
+ .reg = 0x69,
+ .bit = 0x00,
+ }, /* V33S -> V3P3S, display/ssd/audio */
+ {
+ .address = 0x30,
+ .reg = 0x68,
+ .bit = 0x00,
+ }, /* V33U -> V3P3U, SDIO wifi&bt */
+/* {
+ .address = 0x34 .. 0x40,
+ .reg = ??,
+ .bit = ??,
+ }, ** V33I, V18A, REFQ, V12A */
+ {
+ .address = 0x44,
+ .reg = 0x5c,
+ .bit = 0x00,
+ }, /* V18S -> V1P8S, SOC/USB PHY/SIM */
{
.address = 0x48,
.reg = 0x5d,
.bit = 0x00,
- },
+ }, /* V18X -> V1P8SX, eMMC/camara/audio */
+ {
+ .address = 0x4c,
+ .reg = 0x5b,
+ .bit = 0x00,
+ }, /* V18U -> V1P8U, LPDDR */
+ {
+ .address = 0x50,
+ .reg = 0x61,
+ .bit = 0x00,
+ }, /* V12X -> V1P2SX, SOC SFR */
+ {
+ .address = 0x54,
+ .reg = 0x60,
+ .bit = 0x00,
+ }, /* V12S -> V1P2S, MIPI */
+/* {
+ .address = 0x58,
+ .reg = ??,
+ .bit = ??,
+ }, ** V10A */
+ {
+ .address = 0x5c,
+ .reg = 0x56,
+ .bit = 0x00,
+ }, /* V10S -> V1P0S, SOC GFX */
+ {
+ .address = 0x60,
+ .reg = 0x57,
+ .bit = 0x00,
+ }, /* V10X -> V1P0SX, SOC display/DDR IO/PCIe */
+ {
+ .address = 0x64,
+ .reg = 0x59,
+ .bit = 0x00,
+ }, /* V105 -> V1P05S, L2 SRAM */
};
static struct pmic_table thermal_table[] = {
diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c
index 1d50e97d49f1..6d53f7d9fc7a 100644
--- a/drivers/cpufreq/cpufreq_governor.c
+++ b/drivers/cpufreq/cpufreq_governor.c
@@ -555,12 +555,20 @@ EXPORT_SYMBOL_GPL(cpufreq_dbs_governor_stop);
void cpufreq_dbs_governor_limits(struct cpufreq_policy *policy)
{
- struct policy_dbs_info *policy_dbs = policy->governor_data;
+ struct policy_dbs_info *policy_dbs;
+
+ /* Protect gov->gdbs_data against cpufreq_dbs_governor_exit() */
+ mutex_lock(&gov_dbs_data_mutex);
+ policy_dbs = policy->governor_data;
+ if (!policy_dbs)
+ goto out;
mutex_lock(&policy_dbs->update_mutex);
cpufreq_policy_apply_limits(policy);
gov_update_sample_delay(policy_dbs, 0);
-
mutex_unlock(&policy_dbs->update_mutex);
+
+out:
+ mutex_unlock(&gov_dbs_data_mutex);
}
EXPORT_SYMBOL_GPL(cpufreq_dbs_governor_limits);
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index 1aef60d160eb..110483f0e3fb 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -328,9 +328,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
unsigned int polling_threshold;
/*
- * We want to default to C1 (hlt), not to busy polling
- * unless the timer is happening really really soon, or
- * C1's exit latency exceeds the user configured limit.
+ * Default to a physical idle state, not to busy polling, unless
+ * a timer is going to trigger really really soon.
*/
polling_threshold = max_t(unsigned int, 20, s->target_residency);
if (data->next_timer_us > polling_threshold &&
@@ -349,14 +348,12 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
* If the tick is already stopped, the cost of possible short
* idle duration misprediction is much higher, because the CPU
* may be stuck in a shallow idle state for a long time as a
- * result of it. In that case say we might mispredict and try
- * to force the CPU into a state for which we would have stopped
- * the tick, unless a timer is going to expire really soon
- * anyway.
+ * result of it. In that case say we might mispredict and use
+ * the known time till the closest timer event for the idle
+ * state selection.
*/
if (data->predicted_us < TICK_USEC)
- data->predicted_us = min_t(unsigned int, TICK_USEC,
- ktime_to_us(delta_next));
+ data->predicted_us = ktime_to_us(delta_next);
} else {
/*
* Use the performance multiplier and the user-configurable
@@ -381,8 +378,22 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
continue;
if (idx == -1)
idx = i; /* first enabled state */
- if (s->target_residency > data->predicted_us)
- break;
+ if (s->target_residency > data->predicted_us) {
+ if (!tick_nohz_tick_stopped())
+ break;
+
+ /*
+ * If the state selected so far is shallow and this
+ * state's target residency matches the time till the
+ * closest timer event, select this one to avoid getting
+ * stuck in the shallow one for too long.
+ */
+ if (drv->states[idx].target_residency < TICK_USEC &&
+ s->target_residency <= ktime_to_us(delta_next))
+ idx = i;
+
+ goto out;
+ }
if (s->exit_latency > latency_req) {
/*
* If we break out of the loop for latency reasons, use
@@ -403,14 +414,13 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
* Don't stop the tick if the selected state is a polling one or if the
* expected idle duration is shorter than the tick period length.
*/
- if ((drv->states[idx].flags & CPUIDLE_FLAG_POLLING) ||
- expected_interval < TICK_USEC) {
+ if (((drv->states[idx].flags & CPUIDLE_FLAG_POLLING) ||
+ expected_interval < TICK_USEC) && !tick_nohz_tick_stopped()) {
unsigned int delta_next_us = ktime_to_us(delta_next);
*stop_tick = false;
- if (!tick_nohz_tick_stopped() && idx > 0 &&
- drv->states[idx].target_residency > delta_next_us) {
+ if (idx > 0 && drv->states[idx].target_residency > delta_next_us) {
/*
* The tick is not going to be stopped and the target
* residency of the state to be returned is not within
@@ -418,8 +428,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
* tick, so try to correct that.
*/
for (i = idx - 1; i >= 0; i--) {
- if (drv->states[i].disabled ||
- dev->states_usage[i].disable)
+ if (drv->states[i].disabled ||
+ dev->states_usage[i].disable)
continue;
idx = i;
@@ -429,6 +439,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
}
}
+out:
data->last_state_idx = idx;
return data->last_state_idx;
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 4f8df2ec87b1..451d4ae50e66 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -116,24 +116,21 @@ config I2C_I801
DH89xxCC (PCH)
Panther Point (PCH)
Lynx Point (PCH)
- Lynx Point-LP (PCH)
Avoton (SOC)
Wellsburg (PCH)
Coleto Creek (PCH)
Wildcat Point (PCH)
- Wildcat Point-LP (PCH)
BayTrail (SOC)
Braswell (SOC)
- Sunrise Point-H (PCH)
- Sunrise Point-LP (PCH)
- Kaby Lake-H (PCH)
+ Sunrise Point (PCH)
+ Kaby Lake (PCH)
DNV (SOC)
Broxton (SOC)
Lewisburg (PCH)
Gemini Lake (SOC)
- Cannon Lake-H (PCH)
- Cannon Lake-LP (PCH)
+ Cannon Lake (PCH)
Cedar Fork (PCH)
+ Ice Lake (PCH)
This driver can also be built as a module. If so, the module
will be called i2c-i801.
@@ -762,6 +759,13 @@ config I2C_OMAP
Like OMAP1510/1610/1710/5912 and OMAP242x.
For details see http://www.ti.com/omap.
+config I2C_OWL
+ tristate "Actions Semiconductor Owl I2C Controller"
+ depends on ARCH_ACTIONS || COMPILE_TEST
+ help
+ Say Y here if you want to use the I2C bus controller on
+ the Actions Semiconductor Owl SoC's.
+
config I2C_PASEMI
tristate "PA Semi SMBus interface"
depends on PPC_PASEMI && PCI
@@ -828,6 +832,19 @@ config I2C_PXA_SLAVE
is necessary for systems where the PXA may be a target on the
I2C bus.
+config I2C_QCOM_GENI
+ tristate "Qualcomm Technologies Inc.'s GENI based I2C controller"
+ depends on ARCH_QCOM || COMPILE_TEST
+ depends on QCOM_GENI_SE
+ help
+ This driver supports GENI serial engine based I2C controller in
+ master mode on the Qualcomm Technologies Inc.'s SoCs. If you say
+ yes to this option, support will be included for the built-in I2C
+ interface on the Qualcomm Technologies Inc.'s SoCs.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-qcom-geni.
+
config I2C_QUP
tristate "Qualcomm QUP based I2C controller"
depends on ARCH_QCOM
@@ -1330,4 +1347,15 @@ config I2C_ZX2967
This driver can also be built as a module. If so, the module will be
called i2c-zx2967.
+config I2C_FSI
+ tristate "FSI I2C driver"
+ depends on FSI
+ help
+ Driver for FSI bus attached I2C masters. These are I2C masters that
+ are connected to the system over an FSI bus, instead of the more
+ common PCI or MMIO interface.
+
+ This driver can also be built as a module. If so, the module will be
+ called as i2c-fsi.
+
endmenu
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 5a869144a0c5..18b26af82b1c 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -76,6 +76,7 @@ obj-$(CONFIG_I2C_MXS) += i2c-mxs.o
obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o
obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o
obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
+obj-$(CONFIG_I2C_OWL) += i2c-owl.o
obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o
obj-$(CONFIG_I2C_PCA_PLATFORM) += i2c-pca-platform.o
obj-$(CONFIG_I2C_PMCMSP) += i2c-pmcmsp.o
@@ -83,6 +84,7 @@ obj-$(CONFIG_I2C_PNX) += i2c-pnx.o
obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o
obj-$(CONFIG_I2C_PXA) += i2c-pxa.o
obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o
+obj-$(CONFIG_I2C_QCOM_GENI) += i2c-qcom-geni.o
obj-$(CONFIG_I2C_QUP) += i2c-qup.o
obj-$(CONFIG_I2C_RIIC) += i2c-riic.o
obj-$(CONFIG_I2C_RK3X) += i2c-rk3x.o
@@ -137,5 +139,6 @@ obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o
obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
+obj-$(CONFIG_I2C_FSI) += i2c-fsi.o
ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c
index 95a80a8f81b5..134567f3019f 100644
--- a/drivers/i2c/busses/i2c-amd8111.c
+++ b/drivers/i2c/busses/i2c-amd8111.c
@@ -384,6 +384,7 @@ static s32 amd8111_access(struct i2c_adapter * adap, u16 addr,
if (status)
return status;
len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX);
+ /* fall through */
case I2C_SMBUS_I2C_BLOCK_DATA:
for (i = 0; i < len; i++) {
status = amd_ec_read(smbus, AMD_SMB_DATA + i,
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
index 60e4d0e939a3..a4f956c6d567 100644
--- a/drivers/i2c/busses/i2c-aspeed.c
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -111,22 +111,22 @@
#define ASPEED_I2CD_DEV_ADDR_MASK GENMASK(6, 0)
enum aspeed_i2c_master_state {
+ ASPEED_I2C_MASTER_INACTIVE,
ASPEED_I2C_MASTER_START,
ASPEED_I2C_MASTER_TX_FIRST,
ASPEED_I2C_MASTER_TX,
ASPEED_I2C_MASTER_RX_FIRST,
ASPEED_I2C_MASTER_RX,
ASPEED_I2C_MASTER_STOP,
- ASPEED_I2C_MASTER_INACTIVE,
};
enum aspeed_i2c_slave_state {
+ ASPEED_I2C_SLAVE_STOP,
ASPEED_I2C_SLAVE_START,
ASPEED_I2C_SLAVE_READ_REQUESTED,
ASPEED_I2C_SLAVE_READ_PROCESSED,
ASPEED_I2C_SLAVE_WRITE_REQUESTED,
ASPEED_I2C_SLAVE_WRITE_RECEIVED,
- ASPEED_I2C_SLAVE_STOP,
};
struct aspeed_i2c_bus {
@@ -234,7 +234,6 @@ static bool aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus)
bool irq_handled = true;
u8 value;
- spin_lock(&bus->lock);
if (!slave) {
irq_handled = false;
goto out;
@@ -325,7 +324,6 @@ static bool aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus)
writel(status_ack, bus->base + ASPEED_I2C_INTR_STS_REG);
out:
- spin_unlock(&bus->lock);
return irq_handled;
}
#endif /* CONFIG_I2C_SLAVE */
@@ -389,7 +387,6 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
u8 recv_byte;
int ret;
- spin_lock(&bus->lock);
irq_status = readl(bus->base + ASPEED_I2C_INTR_STS_REG);
/* Ack all interrupt bits. */
writel(irq_status, bus->base + ASPEED_I2C_INTR_STS_REG);
@@ -407,7 +404,7 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
*/
ret = aspeed_i2c_is_irq_error(irq_status);
if (ret < 0) {
- dev_dbg(bus->dev, "received error interrupt: 0x%08x",
+ dev_dbg(bus->dev, "received error interrupt: 0x%08x\n",
irq_status);
bus->cmd_err = ret;
bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
@@ -416,7 +413,7 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
/* We are in an invalid state; reset bus to a known state. */
if (!bus->msgs) {
- dev_err(bus->dev, "bus in unknown state");
+ dev_err(bus->dev, "bus in unknown state\n");
bus->cmd_err = -EIO;
if (bus->master_state != ASPEED_I2C_MASTER_STOP)
aspeed_i2c_do_stop(bus);
@@ -431,7 +428,7 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
*/
if (bus->master_state == ASPEED_I2C_MASTER_START) {
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) {
- pr_devel("no slave present at %02x", msg->addr);
+ pr_devel("no slave present at %02x\n", msg->addr);
status_ack |= ASPEED_I2CD_INTR_TX_NAK;
bus->cmd_err = -ENXIO;
aspeed_i2c_do_stop(bus);
@@ -451,11 +448,11 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
switch (bus->master_state) {
case ASPEED_I2C_MASTER_TX:
if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_NAK)) {
- dev_dbg(bus->dev, "slave NACKed TX");
+ dev_dbg(bus->dev, "slave NACKed TX\n");
status_ack |= ASPEED_I2CD_INTR_TX_NAK;
goto error_and_stop;
} else if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) {
- dev_err(bus->dev, "slave failed to ACK TX");
+ dev_err(bus->dev, "slave failed to ACK TX\n");
goto error_and_stop;
}
status_ack |= ASPEED_I2CD_INTR_TX_ACK;
@@ -478,7 +475,7 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
/* fallthrough intended */
case ASPEED_I2C_MASTER_RX:
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_RX_DONE))) {
- dev_err(bus->dev, "master failed to RX");
+ dev_err(bus->dev, "master failed to RX\n");
goto error_and_stop;
}
status_ack |= ASPEED_I2CD_INTR_RX_DONE;
@@ -509,7 +506,7 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
goto out_no_complete;
case ASPEED_I2C_MASTER_STOP:
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_NORMAL_STOP))) {
- dev_err(bus->dev, "master failed to STOP");
+ dev_err(bus->dev, "master failed to STOP\n");
bus->cmd_err = -EIO;
/* Do not STOP as we have already tried. */
} else {
@@ -520,7 +517,7 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
goto out_complete;
case ASPEED_I2C_MASTER_INACTIVE:
dev_err(bus->dev,
- "master received interrupt 0x%08x, but is inactive",
+ "master received interrupt 0x%08x, but is inactive\n",
irq_status);
bus->cmd_err = -EIO;
/* Do not STOP as we should be inactive. */
@@ -547,22 +544,29 @@ out_no_complete:
dev_err(bus->dev,
"irq handled != irq. expected 0x%08x, but was 0x%08x\n",
irq_status, status_ack);
- spin_unlock(&bus->lock);
return !!irq_status;
}
static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
{
struct aspeed_i2c_bus *bus = dev_id;
+ bool ret;
+
+ spin_lock(&bus->lock);
#if IS_ENABLED(CONFIG_I2C_SLAVE)
if (aspeed_i2c_slave_irq(bus)) {
dev_dbg(bus->dev, "irq handled by slave.\n");
- return IRQ_HANDLED;
+ ret = true;
+ goto out;
}
#endif /* CONFIG_I2C_SLAVE */
- return aspeed_i2c_master_irq(bus) ? IRQ_HANDLED : IRQ_NONE;
+ ret = aspeed_i2c_master_irq(bus);
+
+out:
+ spin_unlock(&bus->lock);
+ return ret ? IRQ_HANDLED : IRQ_NONE;
}
static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
@@ -851,7 +855,7 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
bus->rst = devm_reset_control_get_shared(&pdev->dev, NULL);
if (IS_ERR(bus->rst)) {
dev_err(&pdev->dev,
- "missing or invalid reset controller device tree entry");
+ "missing or invalid reset controller device tree entry\n");
return PTR_ERR(bus->rst);
}
reset_control_deassert(bus->rst);
@@ -868,7 +872,7 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
if (!match)
bus->get_clk_reg_val = aspeed_i2c_24xx_get_clk_reg_val;
else
- bus->get_clk_reg_val = match->data;
+ bus->get_clk_reg_val = (u32 (*)(u32))match->data;
/* Initialize the I2C adapter */
spin_lock_init(&bus->lock);
diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c
index 78792b4d6437..826d32049996 100644
--- a/drivers/i2c/busses/i2c-brcmstb.c
+++ b/drivers/i2c/busses/i2c-brcmstb.c
@@ -689,9 +689,9 @@ static int brcmstb_i2c_suspend(struct device *dev)
{
struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev);
- i2c_lock_adapter(&i2c_dev->adapter);
+ i2c_lock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
i2c_dev->is_suspended = true;
- i2c_unlock_adapter(&i2c_dev->adapter);
+ i2c_unlock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
return 0;
}
@@ -700,10 +700,10 @@ static int brcmstb_i2c_resume(struct device *dev)
{
struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev);
- i2c_lock_adapter(&i2c_dev->adapter);
+ i2c_lock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
brcmstb_i2c_set_bsc_reg_defaults(i2c_dev);
i2c_dev->is_suspended = false;
- i2c_unlock_adapter(&i2c_dev->adapter);
+ i2c_unlock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
return 0;
}
diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c
index 7379043711df..11caafa0e050 100644
--- a/drivers/i2c/busses/i2c-davinci.c
+++ b/drivers/i2c/busses/i2c-davinci.c
@@ -718,14 +718,14 @@ static int i2c_davinci_cpufreq_transition(struct notifier_block *nb,
dev = container_of(nb, struct davinci_i2c_dev, freq_transition);
- i2c_lock_adapter(&dev->adapter);
+ i2c_lock_bus(&dev->adapter, I2C_LOCK_ROOT_ADAPTER);
if (val == CPUFREQ_PRECHANGE) {
davinci_i2c_reset_ctrl(dev, 0);
} else if (val == CPUFREQ_POSTCHANGE) {
i2c_davinci_calc_clk_dividers(dev);
davinci_i2c_reset_ctrl(dev, 1);
}
- i2c_unlock_adapter(&dev->adapter);
+ i2c_unlock_bus(&dev->adapter, I2C_LOCK_ROOT_ADAPTER);
return 0;
}
diff --git a/drivers/i2c/busses/i2c-designware-baytrail.c b/drivers/i2c/busses/i2c-designware-baytrail.c
index dbda8c9c8a1c..a2a275cfc1f6 100644
--- a/drivers/i2c/busses/i2c-designware-baytrail.c
+++ b/drivers/i2c/busses/i2c-designware-baytrail.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Intel BayTrail PMIC I2C bus semaphore implementaion
* Copyright (c) 2014, Intel Corporation.
- *
- * 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.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#include <linux/delay.h>
#include <linux/device.h>
diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c
index 48914dfc8ce8..69ec4a791f23 100644
--- a/drivers/i2c/busses/i2c-designware-common.c
+++ b/drivers/i2c/busses/i2c-designware-common.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Synopsys DesignWare I2C adapter driver.
*
@@ -6,20 +7,6 @@
* Copyright (C) 2006 Texas Instruments.
* Copyright (C) 2007 MontaVista Software Inc.
* Copyright (C) 2009 Provigent Ltd.
- *
- * ----------------------------------------------------------------------------
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * ----------------------------------------------------------------------------
- *
*/
#include <linux/clk.h>
#include <linux/delay.h>
@@ -31,6 +18,7 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
+#include <linux/swab.h>
#include "i2c-designware-core.h"
@@ -94,6 +82,40 @@ void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset)
}
}
+/**
+ * i2c_dw_set_reg_access() - Set register access flags
+ * @dev: device private data
+ *
+ * Autodetects needed register access mode and sets access flags accordingly.
+ * This must be called before doing any other register access.
+ */
+int i2c_dw_set_reg_access(struct dw_i2c_dev *dev)
+{
+ u32 reg;
+ int ret;
+
+ ret = i2c_dw_acquire_lock(dev);
+ if (ret)
+ return ret;
+
+ reg = dw_readl(dev, DW_IC_COMP_TYPE);
+ i2c_dw_release_lock(dev);
+
+ if (reg == swab32(DW_IC_COMP_TYPE_VALUE)) {
+ /* Configure register endianess access */
+ dev->flags |= ACCESS_SWAP;
+ } else if (reg == (DW_IC_COMP_TYPE_VALUE & 0x0000ffff)) {
+ /* Configure register access mode 16bit */
+ dev->flags |= ACCESS_16BIT;
+ } else if (reg != DW_IC_COMP_TYPE_VALUE) {
+ dev_err(dev->dev,
+ "Unknown Synopsys component type: 0x%08x\n", reg);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
{
/*
@@ -149,6 +171,47 @@ u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
return ((ic_clk * (tLOW + tf) + 500000) / 1000000) - 1 + offset;
}
+int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev)
+{
+ u32 reg;
+ int ret;
+
+ ret = i2c_dw_acquire_lock(dev);
+ if (ret)
+ return ret;
+
+ /* Configure SDA Hold Time if required */
+ reg = dw_readl(dev, DW_IC_COMP_VERSION);
+ if (reg >= DW_IC_SDA_HOLD_MIN_VERS) {
+ if (!dev->sda_hold_time) {
+ /* Keep previous hold time setting if no one set it */
+ dev->sda_hold_time = dw_readl(dev, DW_IC_SDA_HOLD);
+ }
+
+ /*
+ * Workaround for avoiding TX arbitration lost in case I2C
+ * slave pulls SDA down "too quickly" after falling egde of
+ * SCL by enabling non-zero SDA RX hold. Specification says it
+ * extends incoming SDA low to high transition while SCL is
+ * high but it apprears to help also above issue.
+ */
+ if (!(dev->sda_hold_time & DW_IC_SDA_HOLD_RX_MASK))
+ dev->sda_hold_time |= 1 << DW_IC_SDA_HOLD_RX_SHIFT;
+
+ dev_dbg(dev->dev, "SDA Hold Time TX:RX = %d:%d\n",
+ dev->sda_hold_time & ~(u32)DW_IC_SDA_HOLD_RX_MASK,
+ dev->sda_hold_time >> DW_IC_SDA_HOLD_RX_SHIFT);
+ } else if (dev->sda_hold_time) {
+ dev_warn(dev->dev,
+ "Hardware too old to adjust SDA hold time.\n");
+ dev->sda_hold_time = 0;
+ }
+
+ i2c_dw_release_lock(dev);
+
+ return 0;
+}
+
void __i2c_dw_disable(struct dw_i2c_dev *dev)
{
int timeout = 100;
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index d690e648bc01..e367b1af4ab2 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Synopsys DesignWare I2C adapter driver.
*
@@ -6,20 +7,6 @@
* Copyright (C) 2006 Texas Instruments.
* Copyright (C) 2007 MontaVista Software Inc.
* Copyright (C) 2009 Provigent Ltd.
- *
- * ----------------------------------------------------------------------------
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * ----------------------------------------------------------------------------
- *
*/
#include <linux/i2c.h>
@@ -212,7 +199,8 @@
* @tx_fifo_depth: depth of the hardware tx fifo
* @rx_fifo_depth: depth of the hardware rx fifo
* @rx_outstanding: current master-rx elements in tx fifo
- * @clk_freq: bus clock frequency
+ * @timings: bus clock frequency, SDA hold and other timings
+ * @sda_hold_time: SDA hold value
* @ss_hcnt: standard speed HCNT value
* @ss_lcnt: standard speed LCNT value
* @fs_hcnt: fast speed HCNT value
@@ -264,10 +252,8 @@ struct dw_i2c_dev {
unsigned int tx_fifo_depth;
unsigned int rx_fifo_depth;
int rx_outstanding;
- u32 clk_freq;
+ struct i2c_timings timings;
u32 sda_hold_time;
- u32 sda_falling_time;
- u32 scl_falling_time;
u16 ss_hcnt;
u16 ss_lcnt;
u16 fs_hcnt;
@@ -295,8 +281,10 @@ struct dw_i2c_dev {
u32 dw_readl(struct dw_i2c_dev *dev, int offset);
void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset);
+int i2c_dw_set_reg_access(struct dw_i2c_dev *dev);
u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset);
u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset);
+int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev);
unsigned long i2c_dw_clk_rate(struct dw_i2c_dev *dev);
int i2c_dw_prepare_clk(struct dw_i2c_dev *dev, bool prepare);
int i2c_dw_acquire_lock(struct dw_i2c_dev *dev);
diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c
index 27436a937492..e18442b9973a 100644
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Synopsys DesignWare I2C adapter driver (master only).
*
@@ -6,20 +7,6 @@
* Copyright (C) 2006 Texas Instruments.
* Copyright (C) 2007 MontaVista Software Inc.
* Copyright (C) 2009 Provigent Ltd.
- *
- * ----------------------------------------------------------------------------
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * ----------------------------------------------------------------------------
- *
*/
#include <linux/delay.h>
#include <linux/err.h>
@@ -45,90 +32,79 @@ static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev)
dw_writel(dev, dev->master_cfg, DW_IC_CON);
}
-/**
- * i2c_dw_init() - Initialize the designware I2C master hardware
- * @dev: device private data
- *
- * This functions configures and enables the I2C master.
- * This function is called during I2C init function, and in case of timeout at
- * run time.
- */
-static int i2c_dw_init_master(struct dw_i2c_dev *dev)
+static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
{
- u32 hcnt, lcnt;
- u32 reg, comp_param1;
+ u32 ic_clk = i2c_dw_clk_rate(dev);
+ const char *mode_str, *fp_str = "";
+ u32 comp_param1;
u32 sda_falling_time, scl_falling_time;
+ struct i2c_timings *t = &dev->timings;
int ret;
ret = i2c_dw_acquire_lock(dev);
if (ret)
return ret;
-
- reg = dw_readl(dev, DW_IC_COMP_TYPE);
- if (reg == ___constant_swab32(DW_IC_COMP_TYPE_VALUE)) {
- /* Configure register endianess access */
- dev->flags |= ACCESS_SWAP;
- } else if (reg == (DW_IC_COMP_TYPE_VALUE & 0x0000ffff)) {
- /* Configure register access mode 16bit */
- dev->flags |= ACCESS_16BIT;
- } else if (reg != DW_IC_COMP_TYPE_VALUE) {
- dev_err(dev->dev,
- "Unknown Synopsys component type: 0x%08x\n", reg);
- i2c_dw_release_lock(dev);
- return -ENODEV;
- }
-
comp_param1 = dw_readl(dev, DW_IC_COMP_PARAM_1);
+ i2c_dw_release_lock(dev);
- /* Disable the adapter */
- __i2c_dw_disable(dev);
-
- /* Set standard and fast speed deviders for high/low periods */
-
- sda_falling_time = dev->sda_falling_time ?: 300; /* ns */
- scl_falling_time = dev->scl_falling_time ?: 300; /* ns */
+ /* Set standard and fast speed dividers for high/low periods */
+ sda_falling_time = t->sda_fall_ns ?: 300; /* ns */
+ scl_falling_time = t->scl_fall_ns ?: 300; /* ns */
- /* Set SCL timing parameters for standard-mode */
- if (dev->ss_hcnt && dev->ss_lcnt) {
- hcnt = dev->ss_hcnt;
- lcnt = dev->ss_lcnt;
- } else {
- hcnt = i2c_dw_scl_hcnt(i2c_dw_clk_rate(dev),
+ /* Calculate SCL timing parameters for standard mode if not set */
+ if (!dev->ss_hcnt || !dev->ss_lcnt) {
+ dev->ss_hcnt =
+ i2c_dw_scl_hcnt(ic_clk,
4000, /* tHD;STA = tHIGH = 4.0 us */
sda_falling_time,
0, /* 0: DW default, 1: Ideal */
0); /* No offset */
- lcnt = i2c_dw_scl_lcnt(i2c_dw_clk_rate(dev),
+ dev->ss_lcnt =
+ i2c_dw_scl_lcnt(ic_clk,
4700, /* tLOW = 4.7 us */
scl_falling_time,
0); /* No offset */
}
- dw_writel(dev, hcnt, DW_IC_SS_SCL_HCNT);
- dw_writel(dev, lcnt, DW_IC_SS_SCL_LCNT);
- dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
-
- /* Set SCL timing parameters for fast-mode or fast-mode plus */
- if ((dev->clk_freq == 1000000) && dev->fp_hcnt && dev->fp_lcnt) {
- hcnt = dev->fp_hcnt;
- lcnt = dev->fp_lcnt;
- } else if (dev->fs_hcnt && dev->fs_lcnt) {
- hcnt = dev->fs_hcnt;
- lcnt = dev->fs_lcnt;
- } else {
- hcnt = i2c_dw_scl_hcnt(i2c_dw_clk_rate(dev),
+ dev_dbg(dev->dev, "Standard Mode HCNT:LCNT = %d:%d\n",
+ dev->ss_hcnt, dev->ss_lcnt);
+
+ /*
+ * Set SCL timing parameters for fast mode or fast mode plus. Only
+ * difference is the timing parameter values since the registers are
+ * the same.
+ */
+ if (t->bus_freq_hz == 1000000) {
+ /*
+ * Check are fast mode plus parameters available and use
+ * fast mode if not.
+ */
+ if (dev->fp_hcnt && dev->fp_lcnt) {
+ dev->fs_hcnt = dev->fp_hcnt;
+ dev->fs_lcnt = dev->fp_lcnt;
+ fp_str = " Plus";
+ }
+ }
+ /*
+ * Calculate SCL timing parameters for fast mode if not set. They are
+ * needed also in high speed mode.
+ */
+ if (!dev->fs_hcnt || !dev->fs_lcnt) {
+ dev->fs_hcnt =
+ i2c_dw_scl_hcnt(ic_clk,
600, /* tHD;STA = tHIGH = 0.6 us */
sda_falling_time,
0, /* 0: DW default, 1: Ideal */
0); /* No offset */
- lcnt = i2c_dw_scl_lcnt(i2c_dw_clk_rate(dev),
+ dev->fs_lcnt =
+ i2c_dw_scl_lcnt(ic_clk,
1300, /* tLOW = 1.3 us */
scl_falling_time,
0); /* No offset */
}
- dw_writel(dev, hcnt, DW_IC_FS_SCL_HCNT);
- dw_writel(dev, lcnt, DW_IC_FS_SCL_LCNT);
- dev_dbg(dev->dev, "Fast-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
+ dev_dbg(dev->dev, "Fast Mode%s HCNT:LCNT = %d:%d\n",
+ fp_str, dev->fs_hcnt, dev->fs_lcnt);
+ /* Check is high speed possible and fall back to fast mode if not */
if ((dev->master_cfg & DW_IC_CON_SPEED_MASK) ==
DW_IC_CON_SPEED_HIGH) {
if ((comp_param1 & DW_IC_COMP_PARAM_1_SPEED_MODE_MASK)
@@ -136,37 +112,70 @@ static int i2c_dw_init_master(struct dw_i2c_dev *dev)
dev_err(dev->dev, "High Speed not supported!\n");
dev->master_cfg &= ~DW_IC_CON_SPEED_MASK;
dev->master_cfg |= DW_IC_CON_SPEED_FAST;
+ dev->hs_hcnt = 0;
+ dev->hs_lcnt = 0;
} else if (dev->hs_hcnt && dev->hs_lcnt) {
- hcnt = dev->hs_hcnt;
- lcnt = dev->hs_lcnt;
- dw_writel(dev, hcnt, DW_IC_HS_SCL_HCNT);
- dw_writel(dev, lcnt, DW_IC_HS_SCL_LCNT);
- dev_dbg(dev->dev, "HighSpeed-mode HCNT:LCNT = %d:%d\n",
- hcnt, lcnt);
+ dev_dbg(dev->dev, "High Speed Mode HCNT:LCNT = %d:%d\n",
+ dev->hs_hcnt, dev->hs_lcnt);
}
}
- /* Configure SDA Hold Time if required */
- reg = dw_readl(dev, DW_IC_COMP_VERSION);
- if (reg >= DW_IC_SDA_HOLD_MIN_VERS) {
- if (!dev->sda_hold_time) {
- /* Keep previous hold time setting if no one set it */
- dev->sda_hold_time = dw_readl(dev, DW_IC_SDA_HOLD);
- }
- /*
- * Workaround for avoiding TX arbitration lost in case I2C
- * slave pulls SDA down "too quickly" after falling egde of
- * SCL by enabling non-zero SDA RX hold. Specification says it
- * extends incoming SDA low to high transition while SCL is
- * high but it apprears to help also above issue.
- */
- if (!(dev->sda_hold_time & DW_IC_SDA_HOLD_RX_MASK))
- dev->sda_hold_time |= 1 << DW_IC_SDA_HOLD_RX_SHIFT;
- dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD);
- } else if (dev->sda_hold_time) {
- dev_warn(dev->dev,
- "Hardware too old to adjust SDA hold time.\n");
+ ret = i2c_dw_set_sda_hold(dev);
+ if (ret)
+ goto out;
+
+ switch (dev->master_cfg & DW_IC_CON_SPEED_MASK) {
+ case DW_IC_CON_SPEED_STD:
+ mode_str = "Standard Mode";
+ break;
+ case DW_IC_CON_SPEED_HIGH:
+ mode_str = "High Speed Mode";
+ break;
+ default:
+ mode_str = "Fast Mode";
}
+ dev_dbg(dev->dev, "Bus speed: %s%s\n", mode_str, fp_str);
+
+out:
+ return ret;
+}
+
+/**
+ * i2c_dw_init() - Initialize the designware I2C master hardware
+ * @dev: device private data
+ *
+ * This functions configures and enables the I2C master.
+ * This function is called during I2C init function, and in case of timeout at
+ * run time.
+ */
+static int i2c_dw_init_master(struct dw_i2c_dev *dev)
+{
+ int ret;
+
+ ret = i2c_dw_acquire_lock(dev);
+ if (ret)
+ return ret;
+
+ /* Disable the adapter */
+ __i2c_dw_disable(dev);
+
+ /* Write standard speed timing parameters */
+ dw_writel(dev, dev->ss_hcnt, DW_IC_SS_SCL_HCNT);
+ dw_writel(dev, dev->ss_lcnt, DW_IC_SS_SCL_LCNT);
+
+ /* Write fast mode/fast mode plus timing parameters */
+ dw_writel(dev, dev->fs_hcnt, DW_IC_FS_SCL_HCNT);
+ dw_writel(dev, dev->fs_lcnt, DW_IC_FS_SCL_LCNT);
+
+ /* Write high speed timing parameters if supported */
+ if (dev->hs_hcnt && dev->hs_lcnt) {
+ dw_writel(dev, dev->hs_hcnt, DW_IC_HS_SCL_HCNT);
+ dw_writel(dev, dev->hs_lcnt, DW_IC_HS_SCL_LCNT);
+ }
+
+ /* Write SDA hold time if supported */
+ if (dev->sda_hold_time)
+ dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD);
i2c_dw_configure_fifo_master(dev);
i2c_dw_release_lock(dev);
@@ -253,13 +262,6 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
break;
}
- if (msgs[dev->msg_write_idx].len == 0) {
- dev_err(dev->dev,
- "%s: invalid message length\n", __func__);
- dev->msg_err = -EINVAL;
- break;
- }
-
if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
/* new i2c_msg */
buf = msgs[dev->msg_write_idx].buf;
@@ -502,6 +504,10 @@ static const struct i2c_algorithm i2c_dw_algo = {
.functionality = i2c_dw_func,
};
+static const struct i2c_adapter_quirks i2c_dw_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN,
+};
+
static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
{
u32 stat;
@@ -681,6 +687,14 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)
dev->disable = i2c_dw_disable;
dev->disable_int = i2c_dw_disable_int;
+ ret = i2c_dw_set_reg_access(dev);
+ if (ret)
+ return ret;
+
+ ret = i2c_dw_set_timings_master(dev);
+ if (ret)
+ return ret;
+
ret = dev->init(dev);
if (ret)
return ret;
@@ -689,6 +703,7 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)
"Synopsys DesignWare I2C adapter");
adap->retries = 3;
adap->algo = &i2c_dw_algo;
+ adap->quirks = &i2c_dw_quirks;
adap->dev.parent = dev->dev;
i2c_set_adapdata(adap, dev);
diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c
index 86e1bd0b82e9..d50f80487214 100644
--- a/drivers/i2c/busses/i2c-designware-pcidrv.c
+++ b/drivers/i2c/busses/i2c-designware-pcidrv.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Synopsys DesignWare I2C adapter driver (master only).
*
@@ -7,22 +8,7 @@
* Copyright (C) 2007 MontaVista Software Inc.
* Copyright (C) 2009 Provigent Ltd.
* Copyright (C) 2011, 2015, 2016 Intel Corporation.
- *
- * ----------------------------------------------------------------------------
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * ----------------------------------------------------------------------------
- *
*/
-
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/err.h>
@@ -105,6 +91,7 @@ static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
case 0x0817:
c->bus_cfg &= ~DW_IC_CON_SPEED_MASK;
c->bus_cfg |= DW_IC_CON_SPEED_STD;
+ /* fall through */
case 0x0818:
case 0x0819:
c->bus_num = pdev->device - 0x817 + 3;
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 5660daf6c92e..1a8d2da5b000 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Synopsys DesignWare I2C adapter driver.
*
@@ -6,20 +7,6 @@
* Copyright (C) 2006 Texas Instruments.
* Copyright (C) 2007 MontaVista Software Inc.
* Copyright (C) 2009 Provigent Ltd.
- *
- * ----------------------------------------------------------------------------
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * ----------------------------------------------------------------------------
- *
*/
#include <linux/acpi.h>
#include <linux/clk-provider.h>
@@ -96,6 +83,7 @@ static void dw_i2c_acpi_params(struct platform_device *pdev, char method[],
static int dw_i2c_acpi_configure(struct platform_device *pdev)
{
struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
+ struct i2c_timings *t = &dev->timings;
u32 ss_ht = 0, fp_ht = 0, hs_ht = 0, fs_ht = 0;
acpi_handle handle = ACPI_HANDLE(&pdev->dev);
const struct acpi_device_id *id;
@@ -115,7 +103,7 @@ static int dw_i2c_acpi_configure(struct platform_device *pdev)
dw_i2c_acpi_params(pdev, "HSCN", &dev->hs_hcnt, &dev->hs_lcnt, &hs_ht);
dw_i2c_acpi_params(pdev, "FMCN", &dev->fs_hcnt, &dev->fs_lcnt, &fs_ht);
- switch (dev->clk_freq) {
+ switch (t->bus_freq_hz) {
case 100000:
dev->sda_hold_time = ss_ht;
break;
@@ -175,6 +163,8 @@ static inline int dw_i2c_acpi_configure(struct platform_device *pdev)
static void i2c_dw_configure_master(struct dw_i2c_dev *dev)
{
+ struct i2c_timings *t = &dev->timings;
+
dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;
dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
@@ -182,7 +172,7 @@ static void i2c_dw_configure_master(struct dw_i2c_dev *dev)
dev->mode = DW_IC_MASTER;
- switch (dev->clk_freq) {
+ switch (t->bus_freq_hz) {
case 100000:
dev->master_cfg |= DW_IC_CON_SPEED_STD;
break;
@@ -240,7 +230,8 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct i2c_adapter *adap;
struct dw_i2c_dev *dev;
- u32 acpi_speed, ht = 0;
+ struct i2c_timings *t;
+ u32 acpi_speed;
struct resource *mem;
int i, irq, ret;
static const int supported_speeds[] = {
@@ -272,18 +263,11 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
reset_control_deassert(dev->rst);
}
- if (pdata) {
- dev->clk_freq = pdata->i2c_scl_freq;
- } else {
- device_property_read_u32(&pdev->dev, "i2c-sda-hold-time-ns",
- &ht);
- device_property_read_u32(&pdev->dev, "i2c-sda-falling-time-ns",
- &dev->sda_falling_time);
- device_property_read_u32(&pdev->dev, "i2c-scl-falling-time-ns",
- &dev->scl_falling_time);
- device_property_read_u32(&pdev->dev, "clock-frequency",
- &dev->clk_freq);
- }
+ t = &dev->timings;
+ if (pdata)
+ t->bus_freq_hz = pdata->i2c_scl_freq;
+ else
+ i2c_parse_fw_timings(&pdev->dev, t, false);
acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev);
/*
@@ -300,12 +284,12 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
* Find bus speed from the "clock-frequency" device property, ACPI
* or by using fast mode if neither is set.
*/
- if (acpi_speed && dev->clk_freq)
- dev->clk_freq = min(dev->clk_freq, acpi_speed);
- else if (acpi_speed || dev->clk_freq)
- dev->clk_freq = max(dev->clk_freq, acpi_speed);
+ if (acpi_speed && t->bus_freq_hz)
+ t->bus_freq_hz = min(t->bus_freq_hz, acpi_speed);
+ else if (acpi_speed || t->bus_freq_hz)
+ t->bus_freq_hz = max(t->bus_freq_hz, acpi_speed);
else
- dev->clk_freq = 400000;
+ t->bus_freq_hz = 400000;
if (has_acpi_companion(&pdev->dev))
dw_i2c_acpi_configure(pdev);
@@ -314,11 +298,11 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
* Only standard mode at 100kHz, fast mode at 400kHz,
* fast mode plus at 1MHz and high speed mode at 3.4MHz are supported.
*/
- if (dev->clk_freq != 100000 && dev->clk_freq != 400000
- && dev->clk_freq != 1000000 && dev->clk_freq != 3400000) {
+ if (t->bus_freq_hz != 100000 && t->bus_freq_hz != 400000 &&
+ t->bus_freq_hz != 1000000 && t->bus_freq_hz != 3400000) {
dev_err(&pdev->dev,
"%d Hz is unsupported, only 100kHz, 400kHz, 1MHz and 3.4MHz are supported\n",
- dev->clk_freq);
+ t->bus_freq_hz);
ret = -EINVAL;
goto exit_reset;
}
@@ -334,12 +318,14 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
dev->clk = devm_clk_get(&pdev->dev, NULL);
if (!i2c_dw_prepare_clk(dev, true)) {
+ u64 clk_khz;
+
dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
+ clk_khz = dev->get_clk_rate_khz(dev);
- if (!dev->sda_hold_time && ht)
- dev->sda_hold_time = div_u64(
- (u64)dev->get_clk_rate_khz(dev) * ht + 500000,
- 1000000);
+ if (!dev->sda_hold_time && t->sda_hold_ns)
+ dev->sda_hold_time =
+ div_u64(clk_khz * t->sda_hold_ns + 500000, 1000000);
}
dw_i2c_set_fifo_size(dev, pdev->id);
diff --git a/drivers/i2c/busses/i2c-designware-slave.c b/drivers/i2c/busses/i2c-designware-slave.c
index 8ce2cd368477..e7f9305b2dd9 100644
--- a/drivers/i2c/busses/i2c-designware-slave.c
+++ b/drivers/i2c/busses/i2c-designware-slave.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Synopsys DesignWare I2C adapter driver (slave only).
*
* Based on the Synopsys DesignWare I2C adapter driver (master).
*
* Copyright (C) 2016 Synopsys Inc.
- *
- * ----------------------------------------------------------------------------
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * ----------------------------------------------------------------------------
- *
*/
#include <linux/delay.h>
#include <linux/err.h>
@@ -51,53 +38,18 @@ static void i2c_dw_configure_fifo_slave(struct dw_i2c_dev *dev)
*/
static int i2c_dw_init_slave(struct dw_i2c_dev *dev)
{
- u32 reg, comp_param1;
int ret;
ret = i2c_dw_acquire_lock(dev);
if (ret)
return ret;
- reg = dw_readl(dev, DW_IC_COMP_TYPE);
- if (reg == ___constant_swab32(DW_IC_COMP_TYPE_VALUE)) {
- /* Configure register endianness access. */
- dev->flags |= ACCESS_SWAP;
- } else if (reg == (DW_IC_COMP_TYPE_VALUE & 0x0000ffff)) {
- /* Configure register access mode 16bit. */
- dev->flags |= ACCESS_16BIT;
- } else if (reg != DW_IC_COMP_TYPE_VALUE) {
- dev_err(dev->dev,
- "Unknown Synopsys component type: 0x%08x\n", reg);
- i2c_dw_release_lock(dev);
- return -ENODEV;
- }
-
- comp_param1 = dw_readl(dev, DW_IC_COMP_PARAM_1);
-
/* Disable the adapter. */
__i2c_dw_disable(dev);
- /* Configure SDA Hold Time if required. */
- reg = dw_readl(dev, DW_IC_COMP_VERSION);
- if (reg >= DW_IC_SDA_HOLD_MIN_VERS) {
- if (!dev->sda_hold_time) {
- /* Keep previous hold time setting if no one set it. */
- dev->sda_hold_time = dw_readl(dev, DW_IC_SDA_HOLD);
- }
- /*
- * Workaround for avoiding TX arbitration lost in case I2C
- * slave pulls SDA down "too quickly" after falling egde of
- * SCL by enabling non-zero SDA RX hold. Specification says it
- * extends incoming SDA low to high transition while SCL is
- * high but it apprears to help also above issue.
- */
- if (!(dev->sda_hold_time & DW_IC_SDA_HOLD_RX_MASK))
- dev->sda_hold_time |= 1 << DW_IC_SDA_HOLD_RX_SHIFT;
+ /* Write SDA hold time if supported */
+ if (dev->sda_hold_time)
dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD);
- } else {
- dev_warn(dev->dev,
- "Hardware too old to adjust SDA hold time.\n");
- }
i2c_dw_configure_fifo_slave(dev);
i2c_dw_release_lock(dev);
@@ -299,6 +251,14 @@ int i2c_dw_probe_slave(struct dw_i2c_dev *dev)
dev->disable = i2c_dw_disable;
dev->disable_int = i2c_dw_disable_int;
+ ret = i2c_dw_set_reg_access(dev);
+ if (ret)
+ return ret;
+
+ ret = i2c_dw_set_sda_hold(dev);
+ if (ret)
+ return ret;
+
ret = dev->init(dev);
if (ret)
return ret;
diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c
index de82ad8ff534..c1ce2299a76e 100644
--- a/drivers/i2c/busses/i2c-exynos5.c
+++ b/drivers/i2c/busses/i2c-exynos5.c
@@ -176,7 +176,10 @@
#define EXYNOS5_I2C_TIMEOUT (msecs_to_jiffies(100))
-#define HSI2C_EXYNOS7 BIT(0)
+enum i2c_type_exynos {
+ I2C_TYPE_EXYNOS5,
+ I2C_TYPE_EXYNOS7,
+};
struct exynos5_i2c {
struct i2c_adapter adap;
@@ -212,27 +215,30 @@ struct exynos5_i2c {
/**
* struct exynos_hsi2c_variant - platform specific HSI2C driver data
* @fifo_depth: the fifo depth supported by the HSI2C module
+ * @hw: the hardware variant of Exynos I2C controller
*
* Specifies platform specific configuration of HSI2C module.
* Note: A structure for driver specific platform data is used for future
* expansion of its usage.
*/
struct exynos_hsi2c_variant {
- unsigned int fifo_depth;
- unsigned int hw;
+ unsigned int fifo_depth;
+ enum i2c_type_exynos hw;
};
static const struct exynos_hsi2c_variant exynos5250_hsi2c_data = {
.fifo_depth = 64,
+ .hw = I2C_TYPE_EXYNOS5,
};
static const struct exynos_hsi2c_variant exynos5260_hsi2c_data = {
.fifo_depth = 16,
+ .hw = I2C_TYPE_EXYNOS5,
};
static const struct exynos_hsi2c_variant exynos7_hsi2c_data = {
.fifo_depth = 16,
- .hw = HSI2C_EXYNOS7,
+ .hw = I2C_TYPE_EXYNOS7,
};
static const struct of_device_id exynos5_i2c_match[] = {
@@ -300,7 +306,7 @@ static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, bool hs_timings)
*/
t_ftl_cycle = (readl(i2c->regs + HSI2C_CONF) >> 16) & 0x7;
temp = clkin / op_clk - 8 - t_ftl_cycle;
- if (i2c->variant->hw != HSI2C_EXYNOS7)
+ if (i2c->variant->hw != I2C_TYPE_EXYNOS7)
temp -= t_ftl_cycle;
div = temp / 512;
clk_cycle = temp / (div + 1) - 2;
@@ -424,7 +430,7 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
writel(int_status, i2c->regs + HSI2C_INT_STATUS);
/* handle interrupt related to the transfer status */
- if (i2c->variant->hw == HSI2C_EXYNOS7) {
+ if (i2c->variant->hw == I2C_TYPE_EXYNOS7) {
if (int_status & HSI2C_INT_TRANS_DONE) {
i2c->trans_done = 1;
i2c->state = 0;
@@ -571,7 +577,7 @@ static void exynos5_i2c_bus_check(struct exynos5_i2c *i2c)
{
unsigned long timeout;
- if (i2c->variant->hw != HSI2C_EXYNOS7)
+ if (i2c->variant->hw != I2C_TYPE_EXYNOS7)
return;
/*
@@ -612,7 +618,7 @@ static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
unsigned long flags;
unsigned short trig_lvl;
- if (i2c->variant->hw == HSI2C_EXYNOS7)
+ if (i2c->variant->hw == I2C_TYPE_EXYNOS7)
int_en |= HSI2C_INT_I2C_TRANS;
else
int_en |= HSI2C_INT_I2C;
diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
new file mode 100644
index 000000000000..1e2be2219a60
--- /dev/null
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -0,0 +1,752 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * FSI-attached I2C master algorithm
+ *
+ * Copyright 2018 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/fsi.h>
+#include <linux/i2c.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+#define FSI_ENGID_I2C 0x7
+
+#define I2C_DEFAULT_CLK_DIV 6
+
+/* i2c registers */
+#define I2C_FSI_FIFO 0x00
+#define I2C_FSI_CMD 0x04
+#define I2C_FSI_MODE 0x08
+#define I2C_FSI_WATER_MARK 0x0C
+#define I2C_FSI_INT_MASK 0x10
+#define I2C_FSI_INT_COND 0x14
+#define I2C_FSI_OR_INT_MASK 0x14
+#define I2C_FSI_INTS 0x18
+#define I2C_FSI_AND_INT_MASK 0x18
+#define I2C_FSI_STAT 0x1C
+#define I2C_FSI_RESET_I2C 0x1C
+#define I2C_FSI_ESTAT 0x20
+#define I2C_FSI_RESET_ERR 0x20
+#define I2C_FSI_RESID_LEN 0x24
+#define I2C_FSI_SET_SCL 0x24
+#define I2C_FSI_PORT_BUSY 0x28
+#define I2C_FSI_RESET_SCL 0x2C
+#define I2C_FSI_SET_SDA 0x30
+#define I2C_FSI_RESET_SDA 0x34
+
+/* cmd register */
+#define I2C_CMD_WITH_START BIT(31)
+#define I2C_CMD_WITH_ADDR BIT(30)
+#define I2C_CMD_RD_CONT BIT(29)
+#define I2C_CMD_WITH_STOP BIT(28)
+#define I2C_CMD_FORCELAUNCH BIT(27)
+#define I2C_CMD_ADDR GENMASK(23, 17)
+#define I2C_CMD_READ BIT(16)
+#define I2C_CMD_LEN GENMASK(15, 0)
+
+/* mode register */
+#define I2C_MODE_CLKDIV GENMASK(31, 16)
+#define I2C_MODE_PORT GENMASK(15, 10)
+#define I2C_MODE_ENHANCED BIT(3)
+#define I2C_MODE_DIAG BIT(2)
+#define I2C_MODE_PACE_ALLOW BIT(1)
+#define I2C_MODE_WRAP BIT(0)
+
+/* watermark register */
+#define I2C_WATERMARK_HI GENMASK(15, 12)
+#define I2C_WATERMARK_LO GENMASK(7, 4)
+
+#define I2C_FIFO_HI_LVL 4
+#define I2C_FIFO_LO_LVL 4
+
+/* interrupt register */
+#define I2C_INT_INV_CMD BIT(15)
+#define I2C_INT_PARITY BIT(14)
+#define I2C_INT_BE_OVERRUN BIT(13)
+#define I2C_INT_BE_ACCESS BIT(12)
+#define I2C_INT_LOST_ARB BIT(11)
+#define I2C_INT_NACK BIT(10)
+#define I2C_INT_DAT_REQ BIT(9)
+#define I2C_INT_CMD_COMP BIT(8)
+#define I2C_INT_STOP_ERR BIT(7)
+#define I2C_INT_BUSY BIT(6)
+#define I2C_INT_IDLE BIT(5)
+
+/* status register */
+#define I2C_STAT_INV_CMD BIT(31)
+#define I2C_STAT_PARITY BIT(30)
+#define I2C_STAT_BE_OVERRUN BIT(29)
+#define I2C_STAT_BE_ACCESS BIT(28)
+#define I2C_STAT_LOST_ARB BIT(27)
+#define I2C_STAT_NACK BIT(26)
+#define I2C_STAT_DAT_REQ BIT(25)
+#define I2C_STAT_CMD_COMP BIT(24)
+#define I2C_STAT_STOP_ERR BIT(23)
+#define I2C_STAT_MAX_PORT GENMASK(19, 16)
+#define I2C_STAT_ANY_INT BIT(15)
+#define I2C_STAT_SCL_IN BIT(11)
+#define I2C_STAT_SDA_IN BIT(10)
+#define I2C_STAT_PORT_BUSY BIT(9)
+#define I2C_STAT_SELF_BUSY BIT(8)
+#define I2C_STAT_FIFO_COUNT GENMASK(7, 0)
+
+#define I2C_STAT_ERR (I2C_STAT_INV_CMD | \
+ I2C_STAT_PARITY | \
+ I2C_STAT_BE_OVERRUN | \
+ I2C_STAT_BE_ACCESS | \
+ I2C_STAT_LOST_ARB | \
+ I2C_STAT_NACK | \
+ I2C_STAT_STOP_ERR)
+#define I2C_STAT_ANY_RESP (I2C_STAT_ERR | \
+ I2C_STAT_DAT_REQ | \
+ I2C_STAT_CMD_COMP)
+
+/* extended status register */
+#define I2C_ESTAT_FIFO_SZ GENMASK(31, 24)
+#define I2C_ESTAT_SCL_IN_SY BIT(15)
+#define I2C_ESTAT_SDA_IN_SY BIT(14)
+#define I2C_ESTAT_S_SCL BIT(13)
+#define I2C_ESTAT_S_SDA BIT(12)
+#define I2C_ESTAT_M_SCL BIT(11)
+#define I2C_ESTAT_M_SDA BIT(10)
+#define I2C_ESTAT_HI_WATER BIT(9)
+#define I2C_ESTAT_LO_WATER BIT(8)
+#define I2C_ESTAT_PORT_BUSY BIT(7)
+#define I2C_ESTAT_SELF_BUSY BIT(6)
+#define I2C_ESTAT_VERSION GENMASK(4, 0)
+
+/* port busy register */
+#define I2C_PORT_BUSY_RESET BIT(31)
+
+/* wait for command complete or data request */
+#define I2C_CMD_SLEEP_MAX_US 500
+#define I2C_CMD_SLEEP_MIN_US 50
+
+/* wait after reset; choose time from legacy driver */
+#define I2C_RESET_SLEEP_MAX_US 2000
+#define I2C_RESET_SLEEP_MIN_US 1000
+
+/* choose timeout length from legacy driver; it's well tested */
+#define I2C_ABORT_TIMEOUT msecs_to_jiffies(100)
+
+struct fsi_i2c_master {
+ struct fsi_device *fsi;
+ u8 fifo_size;
+ struct list_head ports;
+ struct mutex lock;
+};
+
+struct fsi_i2c_port {
+ struct list_head list;
+ struct i2c_adapter adapter;
+ struct fsi_i2c_master *master;
+ u16 port;
+ u16 xfrd;
+};
+
+static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
+ u32 *data)
+{
+ int rc;
+ __be32 data_be;
+
+ rc = fsi_device_read(fsi, reg, &data_be, sizeof(data_be));
+ if (rc)
+ return rc;
+
+ *data = be32_to_cpu(data_be);
+
+ return 0;
+}
+
+static int fsi_i2c_write_reg(struct fsi_device *fsi, unsigned int reg,
+ u32 *data)
+{
+ __be32 data_be = cpu_to_be32p(data);
+
+ return fsi_device_write(fsi, reg, &data_be, sizeof(data_be));
+}
+
+static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
+{
+ int rc;
+ u32 mode = I2C_MODE_ENHANCED, extended_status, watermark;
+ u32 interrupt = 0;
+
+ /* since we use polling, disable interrupts */
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_INT_MASK, &interrupt);
+ if (rc)
+ return rc;
+
+ mode |= FIELD_PREP(I2C_MODE_CLKDIV, I2C_DEFAULT_CLK_DIV);
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+ if (rc)
+ return rc;
+
+ rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_ESTAT, &extended_status);
+ if (rc)
+ return rc;
+
+ i2c->fifo_size = FIELD_GET(I2C_ESTAT_FIFO_SZ, extended_status);
+ watermark = FIELD_PREP(I2C_WATERMARK_HI,
+ i2c->fifo_size - I2C_FIFO_HI_LVL);
+ watermark |= FIELD_PREP(I2C_WATERMARK_LO, I2C_FIFO_LO_LVL);
+
+ return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_WATER_MARK, &watermark);
+}
+
+static int fsi_i2c_set_port(struct fsi_i2c_port *port)
+{
+ int rc;
+ struct fsi_device *fsi = port->master->fsi;
+ u32 mode, dummy = 0;
+
+ rc = fsi_i2c_read_reg(fsi, I2C_FSI_MODE, &mode);
+ if (rc)
+ return rc;
+
+ if (FIELD_GET(I2C_MODE_PORT, mode) == port->port)
+ return 0;
+
+ mode = (mode & ~I2C_MODE_PORT) | FIELD_PREP(I2C_MODE_PORT, port->port);
+ rc = fsi_i2c_write_reg(fsi, I2C_FSI_MODE, &mode);
+ if (rc)
+ return rc;
+
+ /* reset engine when port is changed */
+ return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
+}
+
+static int fsi_i2c_start(struct fsi_i2c_port *port, struct i2c_msg *msg,
+ bool stop)
+{
+ struct fsi_i2c_master *i2c = port->master;
+ u32 cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR;
+
+ port->xfrd = 0;
+
+ if (msg->flags & I2C_M_RD)
+ cmd |= I2C_CMD_READ;
+
+ if (stop || msg->flags & I2C_M_STOP)
+ cmd |= I2C_CMD_WITH_STOP;
+
+ cmd |= FIELD_PREP(I2C_CMD_ADDR, msg->addr);
+ cmd |= FIELD_PREP(I2C_CMD_LEN, msg->len);
+
+ return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_CMD, &cmd);
+}
+
+static int fsi_i2c_get_op_bytes(int op_bytes)
+{
+ /* fsi is limited to max 4 byte aligned ops */
+ if (op_bytes > 4)
+ return 4;
+ else if (op_bytes == 3)
+ return 2;
+ return op_bytes;
+}
+
+static int fsi_i2c_write_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
+ u8 fifo_count)
+{
+ int write;
+ int rc;
+ struct fsi_i2c_master *i2c = port->master;
+ int bytes_to_write = i2c->fifo_size - fifo_count;
+ int bytes_remaining = msg->len - port->xfrd;
+
+ bytes_to_write = min(bytes_to_write, bytes_remaining);
+
+ while (bytes_to_write) {
+ write = fsi_i2c_get_op_bytes(bytes_to_write);
+
+ rc = fsi_device_write(i2c->fsi, I2C_FSI_FIFO,
+ &msg->buf[port->xfrd], write);
+ if (rc)
+ return rc;
+
+ port->xfrd += write;
+ bytes_to_write -= write;
+ }
+
+ return 0;
+}
+
+static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
+ u8 fifo_count)
+{
+ int read;
+ int rc;
+ struct fsi_i2c_master *i2c = port->master;
+ int bytes_to_read;
+ int xfr_remaining = msg->len - port->xfrd;
+ u32 dummy;
+
+ bytes_to_read = min_t(int, fifo_count, xfr_remaining);
+
+ while (bytes_to_read) {
+ read = fsi_i2c_get_op_bytes(bytes_to_read);
+
+ if (xfr_remaining) {
+ rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO,
+ &msg->buf[port->xfrd], read);
+ if (rc)
+ return rc;
+
+ port->xfrd += read;
+ xfr_remaining -= read;
+ } else {
+ /* no more buffer but data in fifo, need to clear it */
+ rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO, &dummy,
+ read);
+ if (rc)
+ return rc;
+ }
+
+ bytes_to_read -= read;
+ }
+
+ return 0;
+}
+
+static int fsi_i2c_get_scl(struct i2c_adapter *adap)
+{
+ u32 stat = 0;
+ struct fsi_i2c_port *port = adap->algo_data;
+ struct fsi_i2c_master *i2c = port->master;
+
+ fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
+
+ return !!(stat & I2C_STAT_SCL_IN);
+}
+
+static void fsi_i2c_set_scl(struct i2c_adapter *adap, int val)
+{
+ u32 dummy = 0;
+ struct fsi_i2c_port *port = adap->algo_data;
+ struct fsi_i2c_master *i2c = port->master;
+
+ if (val)
+ fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SCL, &dummy);
+ else
+ fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SCL, &dummy);
+}
+
+static int fsi_i2c_get_sda(struct i2c_adapter *adap)
+{
+ u32 stat = 0;
+ struct fsi_i2c_port *port = adap->algo_data;
+ struct fsi_i2c_master *i2c = port->master;
+
+ fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
+
+ return !!(stat & I2C_STAT_SDA_IN);
+}
+
+static void fsi_i2c_set_sda(struct i2c_adapter *adap, int val)
+{
+ u32 dummy = 0;
+ struct fsi_i2c_port *port = adap->algo_data;
+ struct fsi_i2c_master *i2c = port->master;
+
+ if (val)
+ fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SDA, &dummy);
+ else
+ fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SDA, &dummy);
+}
+
+static void fsi_i2c_prepare_recovery(struct i2c_adapter *adap)
+{
+ int rc;
+ u32 mode;
+ struct fsi_i2c_port *port = adap->algo_data;
+ struct fsi_i2c_master *i2c = port->master;
+
+ rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+ if (rc)
+ return;
+
+ mode |= I2C_MODE_DIAG;
+ fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+}
+
+static void fsi_i2c_unprepare_recovery(struct i2c_adapter *adap)
+{
+ int rc;
+ u32 mode;
+ struct fsi_i2c_port *port = adap->algo_data;
+ struct fsi_i2c_master *i2c = port->master;
+
+ rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+ if (rc)
+ return;
+
+ mode &= ~I2C_MODE_DIAG;
+ fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+}
+
+static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c,
+ struct fsi_i2c_port *port)
+{
+ int rc;
+ u32 stat, dummy = 0;
+
+ /* force bus reset, ignore errors */
+ i2c_recover_bus(&port->adapter);
+
+ /* reset errors */
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_ERR, &dummy);
+ if (rc)
+ return rc;
+
+ /* wait for command complete */
+ usleep_range(I2C_RESET_SLEEP_MIN_US, I2C_RESET_SLEEP_MAX_US);
+
+ rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
+ if (rc)
+ return rc;
+
+ if (stat & I2C_STAT_CMD_COMP)
+ return 0;
+
+ /* failed to get command complete; reset engine again */
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, &dummy);
+ if (rc)
+ return rc;
+
+ /* re-init engine again */
+ return fsi_i2c_dev_init(i2c);
+}
+
+static int fsi_i2c_reset_engine(struct fsi_i2c_master *i2c, u16 port)
+{
+ int rc;
+ u32 mode, dummy = 0;
+
+ /* reset engine */
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, &dummy);
+ if (rc)
+ return rc;
+
+ /* re-init engine */
+ rc = fsi_i2c_dev_init(i2c);
+ if (rc)
+ return rc;
+
+ rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+ if (rc)
+ return rc;
+
+ /* set port; default after reset is 0 */
+ if (port) {
+ mode &= ~I2C_MODE_PORT;
+ mode |= FIELD_PREP(I2C_MODE_PORT, port);
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+ if (rc)
+ return rc;
+ }
+
+ /* reset busy register; hw workaround */
+ dummy = I2C_PORT_BUSY_RESET;
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_PORT_BUSY, &dummy);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status)
+{
+ int rc;
+ unsigned long start;
+ u32 cmd = I2C_CMD_WITH_STOP;
+ u32 stat;
+ struct fsi_i2c_master *i2c = port->master;
+ struct fsi_device *fsi = i2c->fsi;
+
+ rc = fsi_i2c_reset_engine(i2c, port->port);
+ if (rc)
+ return rc;
+
+ rc = fsi_i2c_read_reg(fsi, I2C_FSI_STAT, &stat);
+ if (rc)
+ return rc;
+
+ /* if sda is low, peform full bus reset */
+ if (!(stat & I2C_STAT_SDA_IN)) {
+ rc = fsi_i2c_reset_bus(i2c, port);
+ if (rc)
+ return rc;
+ }
+
+ /* skip final stop command for these errors */
+ if (status & (I2C_STAT_PARITY | I2C_STAT_LOST_ARB | I2C_STAT_STOP_ERR))
+ return 0;
+
+ /* write stop command */
+ rc = fsi_i2c_write_reg(fsi, I2C_FSI_CMD, &cmd);
+ if (rc)
+ return rc;
+
+ /* wait until we see command complete in the master */
+ start = jiffies;
+
+ do {
+ rc = fsi_i2c_read_reg(fsi, I2C_FSI_STAT, &status);
+ if (rc)
+ return rc;
+
+ if (status & I2C_STAT_CMD_COMP)
+ return 0;
+
+ usleep_range(I2C_CMD_SLEEP_MIN_US, I2C_CMD_SLEEP_MAX_US);
+ } while (time_after(start + I2C_ABORT_TIMEOUT, jiffies));
+
+ return -ETIMEDOUT;
+}
+
+static int fsi_i2c_handle_status(struct fsi_i2c_port *port,
+ struct i2c_msg *msg, u32 status)
+{
+ int rc;
+ u8 fifo_count;
+
+ if (status & I2C_STAT_ERR) {
+ rc = fsi_i2c_abort(port, status);
+ if (rc)
+ return rc;
+
+ if (status & I2C_STAT_INV_CMD)
+ return -EINVAL;
+
+ if (status & (I2C_STAT_PARITY | I2C_STAT_BE_OVERRUN |
+ I2C_STAT_BE_ACCESS))
+ return -EPROTO;
+
+ if (status & I2C_STAT_NACK)
+ return -ENXIO;
+
+ if (status & I2C_STAT_LOST_ARB)
+ return -EAGAIN;
+
+ if (status & I2C_STAT_STOP_ERR)
+ return -EBADMSG;
+
+ return -EIO;
+ }
+
+ if (status & I2C_STAT_DAT_REQ) {
+ fifo_count = FIELD_GET(I2C_STAT_FIFO_COUNT, status);
+
+ if (msg->flags & I2C_M_RD)
+ return fsi_i2c_read_fifo(port, msg, fifo_count);
+
+ return fsi_i2c_write_fifo(port, msg, fifo_count);
+ }
+
+ if (status & I2C_STAT_CMD_COMP) {
+ if (port->xfrd < msg->len)
+ return -ENODATA;
+
+ return msg->len;
+ }
+
+ return 0;
+}
+
+static int fsi_i2c_wait(struct fsi_i2c_port *port, struct i2c_msg *msg,
+ unsigned long timeout)
+{
+ u32 status = 0;
+ int rc;
+ unsigned long start = jiffies;
+
+ do {
+ rc = fsi_i2c_read_reg(port->master->fsi, I2C_FSI_STAT,
+ &status);
+ if (rc)
+ return rc;
+
+ if (status & I2C_STAT_ANY_RESP) {
+ rc = fsi_i2c_handle_status(port, msg, status);
+ if (rc < 0)
+ return rc;
+
+ /* cmd complete and all data xfrd */
+ if (rc == msg->len)
+ return 0;
+
+ /* need to xfr more data, but maybe don't need wait */
+ continue;
+ }
+
+ usleep_range(I2C_CMD_SLEEP_MIN_US, I2C_CMD_SLEEP_MAX_US);
+ } while (time_after(start + timeout, jiffies));
+
+ return -ETIMEDOUT;
+}
+
+static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ int i, rc;
+ unsigned long start_time;
+ struct fsi_i2c_port *port = adap->algo_data;
+ struct fsi_i2c_master *master = port->master;
+ struct i2c_msg *msg;
+
+ mutex_lock(&master->lock);
+
+ rc = fsi_i2c_set_port(port);
+ if (rc)
+ goto unlock;
+
+ for (i = 0; i < num; i++) {
+ msg = msgs + i;
+ start_time = jiffies;
+
+ rc = fsi_i2c_start(port, msg, i == num - 1);
+ if (rc)
+ goto unlock;
+
+ rc = fsi_i2c_wait(port, msg,
+ adap->timeout - (jiffies - start_time));
+ if (rc)
+ goto unlock;
+ }
+
+unlock:
+ mutex_unlock(&master->lock);
+ return rc ? : num;
+}
+
+static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_PROTOCOL_MANGLING |
+ I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static struct i2c_bus_recovery_info fsi_i2c_bus_recovery_info = {
+ .recover_bus = i2c_generic_scl_recovery,
+ .get_scl = fsi_i2c_get_scl,
+ .set_scl = fsi_i2c_set_scl,
+ .get_sda = fsi_i2c_get_sda,
+ .set_sda = fsi_i2c_set_sda,
+ .prepare_recovery = fsi_i2c_prepare_recovery,
+ .unprepare_recovery = fsi_i2c_unprepare_recovery,
+};
+
+static const struct i2c_algorithm fsi_i2c_algorithm = {
+ .master_xfer = fsi_i2c_xfer,
+ .functionality = fsi_i2c_functionality,
+};
+
+static int fsi_i2c_probe(struct device *dev)
+{
+ struct fsi_i2c_master *i2c;
+ struct fsi_i2c_port *port;
+ struct device_node *np;
+ int rc;
+ u32 port_no;
+
+ i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ mutex_init(&i2c->lock);
+ i2c->fsi = to_fsi_dev(dev);
+ INIT_LIST_HEAD(&i2c->ports);
+
+ rc = fsi_i2c_dev_init(i2c);
+ if (rc)
+ return rc;
+
+ /* Add adapter for each i2c port of the master. */
+ for_each_available_child_of_node(dev->of_node, np) {
+ rc = of_property_read_u32(np, "reg", &port_no);
+ if (rc || port_no > USHRT_MAX)
+ continue;
+
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ break;
+
+ port->master = i2c;
+ port->port = port_no;
+
+ port->adapter.owner = THIS_MODULE;
+ port->adapter.dev.of_node = np;
+ port->adapter.dev.parent = dev;
+ port->adapter.algo = &fsi_i2c_algorithm;
+ port->adapter.bus_recovery_info = &fsi_i2c_bus_recovery_info;
+ port->adapter.algo_data = port;
+
+ snprintf(port->adapter.name, sizeof(port->adapter.name),
+ "i2c_bus-%u", port_no);
+
+ rc = i2c_add_adapter(&port->adapter);
+ if (rc < 0) {
+ dev_err(dev, "Failed to register adapter: %d\n", rc);
+ kfree(port);
+ continue;
+ }
+
+ list_add(&port->list, &i2c->ports);
+ }
+
+ dev_set_drvdata(dev, i2c);
+
+ return 0;
+}
+
+static int fsi_i2c_remove(struct device *dev)
+{
+ struct fsi_i2c_master *i2c = dev_get_drvdata(dev);
+ struct fsi_i2c_port *port, *tmp;
+
+ list_for_each_entry_safe(port, tmp, &i2c->ports, list) {
+ list_del(&port->list);
+ i2c_del_adapter(&port->adapter);
+ kfree(port);
+ }
+
+ return 0;
+}
+
+static const struct fsi_device_id fsi_i2c_ids[] = {
+ { FSI_ENGID_I2C, FSI_VERSION_ANY },
+ { }
+};
+
+static struct fsi_driver fsi_i2c_driver = {
+ .id_table = fsi_i2c_ids,
+ .drv = {
+ .name = "i2c-fsi",
+ .bus = &fsi_bus_type,
+ .probe = fsi_i2c_probe,
+ .remove = fsi_i2c_remove,
+ },
+};
+
+module_fsi_driver(fsi_i2c_driver);
+
+MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
+MODULE_DESCRIPTION("FSI attached I2C master");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c
index 66f85bbf3591..c008d209f0b8 100644
--- a/drivers/i2c/busses/i2c-gpio.c
+++ b/drivers/i2c/busses/i2c-gpio.c
@@ -78,49 +78,43 @@ static struct dentry *i2c_gpio_debug_dir;
#define getscl(bd) ((bd)->getscl((bd)->data))
#define WIRE_ATTRIBUTE(wire) \
-static int fops_##wire##_get(void *data, u64 *val) \
-{ \
- struct i2c_gpio_private_data *priv = data; \
- \
- i2c_lock_adapter(&priv->adap); \
- *val = get##wire(&priv->bit_data); \
- i2c_unlock_adapter(&priv->adap); \
- return 0; \
-} \
-static int fops_##wire##_set(void *data, u64 val) \
-{ \
- struct i2c_gpio_private_data *priv = data; \
- \
- i2c_lock_adapter(&priv->adap); \
- set##wire(&priv->bit_data, val); \
- i2c_unlock_adapter(&priv->adap); \
- return 0; \
-} \
+static int fops_##wire##_get(void *data, u64 *val) \
+{ \
+ struct i2c_gpio_private_data *priv = data; \
+ \
+ i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); \
+ *val = get##wire(&priv->bit_data); \
+ i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); \
+ return 0; \
+} \
+static int fops_##wire##_set(void *data, u64 val) \
+{ \
+ struct i2c_gpio_private_data *priv = data; \
+ \
+ i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); \
+ set##wire(&priv->bit_data, val); \
+ i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); \
+ return 0; \
+} \
DEFINE_DEBUGFS_ATTRIBUTE(fops_##wire, fops_##wire##_get, fops_##wire##_set, "%llu\n")
WIRE_ATTRIBUTE(scl);
WIRE_ATTRIBUTE(sda);
-static int fops_incomplete_transfer_set(void *data, u64 addr)
+static void i2c_gpio_incomplete_transfer(struct i2c_gpio_private_data *priv,
+ u32 pattern, u8 pattern_size)
{
- struct i2c_gpio_private_data *priv = data;
struct i2c_algo_bit_data *bit_data = &priv->bit_data;
- int i, pattern;
+ int i;
- if (addr > 0x7f)
- return -EINVAL;
-
- /* ADDR (7 bit) + RD (1 bit) + SDA hi (1 bit) */
- pattern = (addr << 2) | 3;
-
- i2c_lock_adapter(&priv->adap);
+ i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER);
/* START condition */
setsda(bit_data, 0);
udelay(bit_data->udelay);
- /* Send ADDR+RD, request ACK, don't send STOP */
- for (i = 8; i >= 0; i--) {
+ /* Send pattern, request ACK, don't send STOP */
+ for (i = pattern_size - 1; i >= 0; i--) {
setscl(bit_data, 0);
udelay(bit_data->udelay / 2);
setsda(bit_data, (pattern >> i) & 1);
@@ -129,11 +123,44 @@ static int fops_incomplete_transfer_set(void *data, u64 addr)
udelay(bit_data->udelay);
}
- i2c_unlock_adapter(&priv->adap);
+ i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER);
+}
+
+static int fops_incomplete_addr_phase_set(void *data, u64 addr)
+{
+ struct i2c_gpio_private_data *priv = data;
+ u32 pattern;
+
+ if (addr > 0x7f)
+ return -EINVAL;
+
+ /* ADDR (7 bit) + RD (1 bit) + Client ACK, keep SDA hi (1 bit) */
+ pattern = (addr << 2) | 3;
+
+ i2c_gpio_incomplete_transfer(priv, pattern, 9);
+
+ return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_addr_phase, NULL, fops_incomplete_addr_phase_set, "%llu\n");
+
+static int fops_incomplete_write_byte_set(void *data, u64 addr)
+{
+ struct i2c_gpio_private_data *priv = data;
+ u32 pattern;
+
+ if (addr > 0x7f)
+ return -EINVAL;
+
+ /* ADDR (7 bit) + WR (1 bit) + Client ACK (1 bit) */
+ pattern = (addr << 2) | 1;
+ /* 0x00 (8 bit) + Client ACK, keep SDA hi (1 bit) */
+ pattern = (pattern << 9) | 1;
+
+ i2c_gpio_incomplete_transfer(priv, pattern, 18);
return 0;
}
-DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_transfer, NULL, fops_incomplete_transfer_set, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_write_byte, NULL, fops_incomplete_write_byte_set, "%llu\n");
static void i2c_gpio_fault_injector_init(struct platform_device *pdev)
{
@@ -156,8 +183,10 @@ static void i2c_gpio_fault_injector_init(struct platform_device *pdev)
debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl);
debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda);
- debugfs_create_file_unsafe("incomplete_transfer", 0200, priv->debug_dir,
- priv, &fops_incomplete_transfer);
+ debugfs_create_file_unsafe("incomplete_address_phase", 0200, priv->debug_dir,
+ priv, &fops_incomplete_addr_phase);
+ debugfs_create_file_unsafe("incomplete_write_byte", 0200, priv->debug_dir,
+ priv, &fops_incomplete_write_byte);
}
static void i2c_gpio_fault_injector_exit(struct platform_device *pdev)
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index aa726607645e..941c223f6491 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -70,6 +70,7 @@
* Cannon Lake-H (PCH) 0xa323 32 hard yes yes yes
* Cannon Lake-LP (PCH) 0x9da3 32 hard yes yes yes
* Cedar Fork (PCH) 0x18df 32 hard yes yes yes
+ * Ice Lake-LP (PCH) 0x34a3 32 hard yes yes yes
*
* Features supported by this driver:
* Software PEC no
@@ -220,6 +221,7 @@
#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330
#define PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS 0x23b0
#define PCI_DEVICE_ID_INTEL_GEMINILAKE_SMBUS 0x31d4
+#define PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS 0x34a3
#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30
#define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
@@ -1034,6 +1036,7 @@ static const struct pci_device_id i801_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CANNONLAKE_H_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CANNONLAKE_LP_SMBUS) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS) },
{ 0, }
};
@@ -1518,6 +1521,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
case PCI_DEVICE_ID_INTEL_CDF_SMBUS:
case PCI_DEVICE_ID_INTEL_DNV_SMBUS:
case PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS:
+ case PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS:
priv->features |= FEATURE_I2C_BLOCK_READ;
priv->features |= FEATURE_IRQ;
priv->features |= FEATURE_SMBUS_PEC;
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 498c5e891649..c406700789e1 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -421,10 +421,14 @@ static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy)
return -EAGAIN;
}
- if (for_busy && (temp & I2SR_IBB))
+ if (for_busy && (temp & I2SR_IBB)) {
+ i2c_imx->stopped = 0;
break;
- if (!for_busy && !(temp & I2SR_IBB))
+ }
+ if (!for_busy && !(temp & I2SR_IBB)) {
+ i2c_imx->stopped = 1;
break;
+ }
if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
dev_dbg(&i2c_imx->adapter.dev,
"<%s> I2C bus is busy\n", __func__);
@@ -538,7 +542,6 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
result = i2c_imx_bus_busy(i2c_imx, 1);
if (result)
return result;
- i2c_imx->stopped = 0;
temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK;
temp &= ~I2CR_DMAEN;
@@ -567,10 +570,8 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
udelay(i2c_imx->disable_delay);
}
- if (!i2c_imx->stopped) {
+ if (!i2c_imx->stopped)
i2c_imx_bus_busy(i2c_imx, 0);
- i2c_imx->stopped = 1;
- }
/* Disable I2C controller */
temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
@@ -668,9 +669,6 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
struct imx_i2c_dma *dma = i2c_imx->dma;
struct device *dev = &i2c_imx->adapter.dev;
- temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
- temp |= I2CR_DMAEN;
- imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
dma->chan_using = dma->chan_rx;
dma->dma_transfer_dir = DMA_DEV_TO_MEM;
@@ -727,7 +725,6 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
temp &= ~(I2CR_MSTA | I2CR_MTX);
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
i2c_imx_bus_busy(i2c_imx, 0);
- i2c_imx->stopped = 1;
} else {
/*
* For i2c master receiver repeat restart operation like:
@@ -783,6 +780,7 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo
int i, result;
unsigned int temp;
int block_data = msgs->flags & I2C_M_RECV_LEN;
+ int use_dma = i2c_imx->dma && msgs->len >= DMA_THRESHOLD && !block_data;
dev_dbg(&i2c_imx->adapter.dev,
"<%s> write slave address: addr=0x%x\n",
@@ -809,12 +807,14 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo
*/
if ((msgs->len - 1) || block_data)
temp &= ~I2CR_TXAK;
+ if (use_dma)
+ temp |= I2CR_DMAEN;
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); /* dummy read */
dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__);
- if (i2c_imx->dma && msgs->len >= DMA_THRESHOLD && !block_data)
+ if (use_dma)
return i2c_imx_dma_read(i2c_imx, msgs, is_lastmsg);
/* read data */
@@ -850,7 +850,6 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo
temp &= ~(I2CR_MSTA | I2CR_MTX);
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
i2c_imx_bus_busy(i2c_imx, 0);
- i2c_imx->stopped = 1;
} else {
/*
* For i2c master receiver repeat restart operation like:
diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c
index 642c58946d8d..7d79317a1046 100644
--- a/drivers/i2c/busses/i2c-mxs.c
+++ b/drivers/i2c/busses/i2c-mxs.c
@@ -567,9 +567,6 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg,
dev_dbg(i2c->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
msg->addr, msg->len, msg->flags, stop);
- if (msg->len == 0)
- return -EINVAL;
-
/*
* The MX28 I2C IP block can only do PIO READ for transfer of to up
* 4 bytes of length. The write transfer is not limited as it can use
@@ -683,6 +680,10 @@ static const struct i2c_algorithm mxs_i2c_algo = {
.functionality = mxs_i2c_func,
};
+static const struct i2c_adapter_quirks mxs_i2c_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN,
+};
+
static void mxs_i2c_derive_timing(struct mxs_i2c_dev *i2c, uint32_t speed)
{
/* The I2C block clock runs at 24MHz */
@@ -854,6 +855,7 @@ static int mxs_i2c_probe(struct platform_device *pdev)
strlcpy(adap->name, "MXS I2C adapter", sizeof(adap->name));
adap->owner = THIS_MODULE;
adap->algo = &mxs_i2c_algo;
+ adap->quirks = &mxs_i2c_quirks;
adap->dev.parent = dev;
adap->nr = pdev->id;
adap->dev.of_node = pdev->dev.of_node;
diff --git a/drivers/i2c/busses/i2c-owl.c b/drivers/i2c/busses/i2c-owl.c
new file mode 100644
index 000000000000..96b4572e6d9c
--- /dev/null
+++ b/drivers/i2c/busses/i2c-owl.c
@@ -0,0 +1,495 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Actions Semiconductor Owl SoC's I2C driver
+ *
+ * Copyright (c) 2014 Actions Semi Inc.
+ * Author: David Liu <liuwei@actions-semi.com>
+ *
+ * Copyright (c) 2018 Linaro Ltd.
+ * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+/* I2C registers */
+#define OWL_I2C_REG_CTL 0x0000
+#define OWL_I2C_REG_CLKDIV 0x0004
+#define OWL_I2C_REG_STAT 0x0008
+#define OWL_I2C_REG_ADDR 0x000C
+#define OWL_I2C_REG_TXDAT 0x0010
+#define OWL_I2C_REG_RXDAT 0x0014
+#define OWL_I2C_REG_CMD 0x0018
+#define OWL_I2C_REG_FIFOCTL 0x001C
+#define OWL_I2C_REG_FIFOSTAT 0x0020
+#define OWL_I2C_REG_DATCNT 0x0024
+#define OWL_I2C_REG_RCNT 0x0028
+
+/* I2Cx_CTL Bit Mask */
+#define OWL_I2C_CTL_RB BIT(1)
+#define OWL_I2C_CTL_GBCC(x) (((x) & 0x3) << 2)
+#define OWL_I2C_CTL_GBCC_NONE OWL_I2C_CTL_GBCC(0)
+#define OWL_I2C_CTL_GBCC_START OWL_I2C_CTL_GBCC(1)
+#define OWL_I2C_CTL_GBCC_STOP OWL_I2C_CTL_GBCC(2)
+#define OWL_I2C_CTL_GBCC_RSTART OWL_I2C_CTL_GBCC(3)
+#define OWL_I2C_CTL_IRQE BIT(5)
+#define OWL_I2C_CTL_EN BIT(7)
+#define OWL_I2C_CTL_AE BIT(8)
+#define OWL_I2C_CTL_SHSM BIT(10)
+
+#define OWL_I2C_DIV_FACTOR(x) ((x) & 0xff)
+
+/* I2Cx_STAT Bit Mask */
+#define OWL_I2C_STAT_RACK BIT(0)
+#define OWL_I2C_STAT_BEB BIT(1)
+#define OWL_I2C_STAT_IRQP BIT(2)
+#define OWL_I2C_STAT_LAB BIT(3)
+#define OWL_I2C_STAT_STPD BIT(4)
+#define OWL_I2C_STAT_STAD BIT(5)
+#define OWL_I2C_STAT_BBB BIT(6)
+#define OWL_I2C_STAT_TCB BIT(7)
+#define OWL_I2C_STAT_LBST BIT(8)
+#define OWL_I2C_STAT_SAMB BIT(9)
+#define OWL_I2C_STAT_SRGC BIT(10)
+
+/* I2Cx_CMD Bit Mask */
+#define OWL_I2C_CMD_SBE BIT(0)
+#define OWL_I2C_CMD_RBE BIT(4)
+#define OWL_I2C_CMD_DE BIT(8)
+#define OWL_I2C_CMD_NS BIT(9)
+#define OWL_I2C_CMD_SE BIT(10)
+#define OWL_I2C_CMD_MSS BIT(11)
+#define OWL_I2C_CMD_WRS BIT(12)
+#define OWL_I2C_CMD_SECL BIT(15)
+
+#define OWL_I2C_CMD_AS(x) (((x) & 0x7) << 1)
+#define OWL_I2C_CMD_SAS(x) (((x) & 0x7) << 5)
+
+/* I2Cx_FIFOCTL Bit Mask */
+#define OWL_I2C_FIFOCTL_NIB BIT(0)
+#define OWL_I2C_FIFOCTL_RFR BIT(1)
+#define OWL_I2C_FIFOCTL_TFR BIT(2)
+
+/* I2Cc_FIFOSTAT Bit Mask */
+#define OWL_I2C_FIFOSTAT_RNB BIT(1)
+#define OWL_I2C_FIFOSTAT_RFE BIT(2)
+#define OWL_I2C_FIFOSTAT_TFF BIT(5)
+#define OWL_I2C_FIFOSTAT_TFD GENMASK(23, 16)
+#define OWL_I2C_FIFOSTAT_RFD GENMASK(15, 8)
+
+/* I2C bus timeout */
+#define OWL_I2C_TIMEOUT msecs_to_jiffies(4 * 1000)
+
+#define OWL_I2C_MAX_RETRIES 50
+
+#define OWL_I2C_DEF_SPEED_HZ 100000
+#define OWL_I2C_MAX_SPEED_HZ 400000
+
+struct owl_i2c_dev {
+ struct i2c_adapter adap;
+ struct i2c_msg *msg;
+ struct completion msg_complete;
+ struct clk *clk;
+ spinlock_t lock;
+ void __iomem *base;
+ unsigned long clk_rate;
+ u32 bus_freq;
+ u32 msg_ptr;
+ int err;
+};
+
+static void owl_i2c_update_reg(void __iomem *reg, unsigned int val, bool state)
+{
+ unsigned int regval;
+
+ regval = readl(reg);
+
+ if (state)
+ regval |= val;
+ else
+ regval &= ~val;
+
+ writel(regval, reg);
+}
+
+static void owl_i2c_reset(struct owl_i2c_dev *i2c_dev)
+{
+ owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL,
+ OWL_I2C_CTL_EN, false);
+ mdelay(1);
+ owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL,
+ OWL_I2C_CTL_EN, true);
+
+ /* Clear status registers */
+ writel(0, i2c_dev->base + OWL_I2C_REG_STAT);
+}
+
+static int owl_i2c_reset_fifo(struct owl_i2c_dev *i2c_dev)
+{
+ unsigned int val, timeout = 0;
+
+ /* Reset FIFO */
+ owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOCTL,
+ OWL_I2C_FIFOCTL_RFR | OWL_I2C_FIFOCTL_TFR,
+ true);
+
+ /* Wait 50ms for FIFO reset complete */
+ do {
+ val = readl(i2c_dev->base + OWL_I2C_REG_FIFOCTL);
+ if (!(val & (OWL_I2C_FIFOCTL_RFR | OWL_I2C_FIFOCTL_TFR)))
+ break;
+ usleep_range(500, 1000);
+ } while (timeout++ < OWL_I2C_MAX_RETRIES);
+
+ if (timeout > OWL_I2C_MAX_RETRIES) {
+ dev_err(&i2c_dev->adap.dev, "FIFO reset timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void owl_i2c_set_freq(struct owl_i2c_dev *i2c_dev)
+{
+ unsigned int val;
+
+ val = DIV_ROUND_UP(i2c_dev->clk_rate, i2c_dev->bus_freq * 16);
+
+ /* Set clock divider factor */
+ writel(OWL_I2C_DIV_FACTOR(val), i2c_dev->base + OWL_I2C_REG_CLKDIV);
+}
+
+static irqreturn_t owl_i2c_interrupt(int irq, void *_dev)
+{
+ struct owl_i2c_dev *i2c_dev = _dev;
+ struct i2c_msg *msg = i2c_dev->msg;
+ unsigned long flags;
+ unsigned int stat, fifostat;
+
+ spin_lock_irqsave(&i2c_dev->lock, flags);
+
+ i2c_dev->err = 0;
+
+ /* Handle NACK from slave */
+ fifostat = readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT);
+ if (fifostat & OWL_I2C_FIFOSTAT_RNB) {
+ i2c_dev->err = -ENXIO;
+ goto stop;
+ }
+
+ /* Handle bus error */
+ stat = readl(i2c_dev->base + OWL_I2C_REG_STAT);
+ if (stat & OWL_I2C_STAT_BEB) {
+ i2c_dev->err = -EIO;
+ goto stop;
+ }
+
+ /* Handle FIFO read */
+ if (msg->flags & I2C_M_RD) {
+ while ((readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT) &
+ OWL_I2C_FIFOSTAT_RFE) && i2c_dev->msg_ptr < msg->len) {
+ msg->buf[i2c_dev->msg_ptr++] = readl(i2c_dev->base +
+ OWL_I2C_REG_RXDAT);
+ }
+ } else {
+ /* Handle the remaining bytes which were not sent */
+ while (!(readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT) &
+ OWL_I2C_FIFOSTAT_TFF) && i2c_dev->msg_ptr < msg->len) {
+ writel(msg->buf[i2c_dev->msg_ptr++],
+ i2c_dev->base + OWL_I2C_REG_TXDAT);
+ }
+ }
+
+stop:
+ /* Clear pending interrupts */
+ owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_STAT,
+ OWL_I2C_STAT_IRQP, true);
+
+ complete_all(&i2c_dev->msg_complete);
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static u32 owl_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static int owl_i2c_check_bus_busy(struct i2c_adapter *adap)
+{
+ struct owl_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+ unsigned long timeout;
+
+ /* Check for Bus busy */
+ timeout = jiffies + OWL_I2C_TIMEOUT;
+ while (readl(i2c_dev->base + OWL_I2C_REG_STAT) & OWL_I2C_STAT_BBB) {
+ if (time_after(jiffies, timeout)) {
+ dev_err(&adap->dev, "Bus busy timeout\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+static int owl_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct owl_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+ struct i2c_msg *msg;
+ unsigned long time_left, flags;
+ unsigned int i2c_cmd, val;
+ unsigned int addr;
+ int ret, idx;
+
+ spin_lock_irqsave(&i2c_dev->lock, flags);
+
+ /* Reset I2C controller */
+ owl_i2c_reset(i2c_dev);
+
+ /* Set bus frequency */
+ owl_i2c_set_freq(i2c_dev);
+
+ /*
+ * Spinlock should be released before calling reset FIFO and
+ * bus busy check since those functions may sleep
+ */
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+
+ /* Reset FIFO */
+ ret = owl_i2c_reset_fifo(i2c_dev);
+ if (ret)
+ goto unlocked_err_exit;
+
+ /* Check for bus busy */
+ ret = owl_i2c_check_bus_busy(adap);
+ if (ret)
+ goto unlocked_err_exit;
+
+ spin_lock_irqsave(&i2c_dev->lock, flags);
+
+ /* Check for Arbitration lost */
+ val = readl(i2c_dev->base + OWL_I2C_REG_STAT);
+ if (val & OWL_I2C_STAT_LAB) {
+ val &= ~OWL_I2C_STAT_LAB;
+ writel(val, i2c_dev->base + OWL_I2C_REG_STAT);
+ ret = -EAGAIN;
+ goto err_exit;
+ }
+
+ reinit_completion(&i2c_dev->msg_complete);
+
+ /* Enable I2C controller interrupt */
+ owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL,
+ OWL_I2C_CTL_IRQE, true);
+
+ /*
+ * Select: FIFO enable, Master mode, Stop enable, Data count enable,
+ * Send start bit
+ */
+ i2c_cmd = OWL_I2C_CMD_SECL | OWL_I2C_CMD_MSS | OWL_I2C_CMD_SE |
+ OWL_I2C_CMD_NS | OWL_I2C_CMD_DE | OWL_I2C_CMD_SBE;
+
+ /* Handle repeated start condition */
+ if (num > 1) {
+ /* Set internal address length and enable repeated start */
+ i2c_cmd |= OWL_I2C_CMD_AS(msgs[0].len + 1) |
+ OWL_I2C_CMD_SAS(1) | OWL_I2C_CMD_RBE;
+
+ /* Write slave address */
+ addr = i2c_8bit_addr_from_msg(&msgs[0]);
+ writel(addr, i2c_dev->base + OWL_I2C_REG_TXDAT);
+
+ /* Write internal register address */
+ for (idx = 0; idx < msgs[0].len; idx++)
+ writel(msgs[0].buf[idx],
+ i2c_dev->base + OWL_I2C_REG_TXDAT);
+
+ msg = &msgs[1];
+ } else {
+ /* Set address length */
+ i2c_cmd |= OWL_I2C_CMD_AS(1);
+ msg = &msgs[0];
+ }
+
+ i2c_dev->msg = msg;
+ i2c_dev->msg_ptr = 0;
+
+ /* Set data count for the message */
+ writel(msg->len, i2c_dev->base + OWL_I2C_REG_DATCNT);
+
+ addr = i2c_8bit_addr_from_msg(msg);
+ writel(addr, i2c_dev->base + OWL_I2C_REG_TXDAT);
+
+ if (!(msg->flags & I2C_M_RD)) {
+ /* Write data to FIFO */
+ for (idx = 0; idx < msg->len; idx++) {
+ /* Check for FIFO full */
+ if (readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT) &
+ OWL_I2C_FIFOSTAT_TFF)
+ break;
+
+ writel(msg->buf[idx],
+ i2c_dev->base + OWL_I2C_REG_TXDAT);
+ }
+
+ i2c_dev->msg_ptr = idx;
+ }
+
+ /* Ignore the NACK if needed */
+ if (msg->flags & I2C_M_IGNORE_NAK)
+ owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOCTL,
+ OWL_I2C_FIFOCTL_NIB, true);
+ else
+ owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOCTL,
+ OWL_I2C_FIFOCTL_NIB, false);
+
+ /* Start the transfer */
+ writel(i2c_cmd, i2c_dev->base + OWL_I2C_REG_CMD);
+
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+
+ time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
+ adap->timeout);
+
+ spin_lock_irqsave(&i2c_dev->lock, flags);
+ if (time_left == 0) {
+ dev_err(&adap->dev, "Transaction timed out\n");
+ /* Send stop condition and release the bus */
+ owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL,
+ OWL_I2C_CTL_GBCC_STOP | OWL_I2C_CTL_RB,
+ true);
+ ret = -ETIMEDOUT;
+ goto err_exit;
+ }
+
+ ret = i2c_dev->err < 0 ? i2c_dev->err : num;
+
+err_exit:
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+
+unlocked_err_exit:
+ /* Disable I2C controller */
+ owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL,
+ OWL_I2C_CTL_EN, false);
+
+ return ret;
+}
+
+static const struct i2c_algorithm owl_i2c_algorithm = {
+ .master_xfer = owl_i2c_master_xfer,
+ .functionality = owl_i2c_func,
+};
+
+static const struct i2c_adapter_quirks owl_i2c_quirks = {
+ .flags = I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST,
+ .max_read_len = 240,
+ .max_write_len = 240,
+ .max_comb_1st_msg_len = 6,
+ .max_comb_2nd_msg_len = 240,
+};
+
+static int owl_i2c_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct owl_i2c_dev *i2c_dev;
+ struct resource *res;
+ int ret, irq;
+
+ i2c_dev = devm_kzalloc(dev, sizeof(*i2c_dev), GFP_KERNEL);
+ if (!i2c_dev)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ i2c_dev->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(i2c_dev->base))
+ return PTR_ERR(i2c_dev->base);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "failed to get IRQ number\n");
+ return irq;
+ }
+
+ if (of_property_read_u32(dev->of_node, "clock-frequency",
+ &i2c_dev->bus_freq))
+ i2c_dev->bus_freq = OWL_I2C_DEF_SPEED_HZ;
+
+ /* We support only frequencies of 100k and 400k for now */
+ if (i2c_dev->bus_freq != OWL_I2C_DEF_SPEED_HZ &&
+ i2c_dev->bus_freq != OWL_I2C_MAX_SPEED_HZ) {
+ dev_err(dev, "invalid clock-frequency %d\n", i2c_dev->bus_freq);
+ return -EINVAL;
+ }
+
+ i2c_dev->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(i2c_dev->clk)) {
+ dev_err(dev, "failed to get clock\n");
+ return PTR_ERR(i2c_dev->clk);
+ }
+
+ ret = clk_prepare_enable(i2c_dev->clk);
+ if (ret)
+ return ret;
+
+ i2c_dev->clk_rate = clk_get_rate(i2c_dev->clk);
+ if (!i2c_dev->clk_rate) {
+ dev_err(dev, "input clock rate should not be zero\n");
+ ret = -EINVAL;
+ goto disable_clk;
+ }
+
+ init_completion(&i2c_dev->msg_complete);
+ spin_lock_init(&i2c_dev->lock);
+ i2c_dev->adap.owner = THIS_MODULE;
+ i2c_dev->adap.algo = &owl_i2c_algorithm;
+ i2c_dev->adap.timeout = OWL_I2C_TIMEOUT;
+ i2c_dev->adap.quirks = &owl_i2c_quirks;
+ i2c_dev->adap.dev.parent = dev;
+ i2c_dev->adap.dev.of_node = dev->of_node;
+ snprintf(i2c_dev->adap.name, sizeof(i2c_dev->adap.name),
+ "%s", "OWL I2C adapter");
+ i2c_set_adapdata(&i2c_dev->adap, i2c_dev);
+
+ platform_set_drvdata(pdev, i2c_dev);
+
+ ret = devm_request_irq(dev, irq, owl_i2c_interrupt, 0, pdev->name,
+ i2c_dev);
+ if (ret) {
+ dev_err(dev, "failed to request irq %d\n", irq);
+ goto disable_clk;
+ }
+
+ return i2c_add_adapter(&i2c_dev->adap);
+
+disable_clk:
+ clk_disable_unprepare(i2c_dev->clk);
+
+ return ret;
+}
+
+static const struct of_device_id owl_i2c_of_match[] = {
+ { .compatible = "actions,s900-i2c" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, owl_i2c_of_match);
+
+static struct platform_driver owl_i2c_driver = {
+ .probe = owl_i2c_probe,
+ .driver = {
+ .name = "owl-i2c",
+ .of_match_table = of_match_ptr(owl_i2c_of_match),
+ },
+};
+module_platform_driver(owl_i2c_driver);
+
+MODULE_AUTHOR("David Liu <liuwei@actions-semi.com>");
+MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
+MODULE_DESCRIPTION("Actions Semiconductor Owl SoC's I2C driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-pasemi.c b/drivers/i2c/busses/i2c-pasemi.c
index 55fd5c6f3cca..50803e5d995b 100644
--- a/drivers/i2c/busses/i2c-pasemi.c
+++ b/drivers/i2c/busses/i2c-pasemi.c
@@ -365,7 +365,6 @@ static int pasemi_smb_probe(struct pci_dev *dev,
smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
smbus->adapter.algo = &smbus_algorithm;
smbus->adapter.algo_data = smbus;
- smbus->adapter.nr = PCI_FUNC(dev->devfn);
/* set up the sysfs linkage to our parent device */
smbus->adapter.dev.parent = &dev->dev;
@@ -373,7 +372,7 @@ static int pasemi_smb_probe(struct pci_dev *dev,
reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR |
(CLK_100K_DIV & CTL_CLK_M)));
- error = i2c_add_numbered_adapter(&smbus->adapter);
+ error = i2c_add_adapter(&smbus->adapter);
if (error)
goto out_release_region;
diff --git a/drivers/i2c/busses/i2c-pmcmsp.c b/drivers/i2c/busses/i2c-pmcmsp.c
index dae8ac618a52..0829cb696d9d 100644
--- a/drivers/i2c/busses/i2c-pmcmsp.c
+++ b/drivers/i2c/busses/i2c-pmcmsp.c
@@ -444,16 +444,6 @@ static enum pmcmsptwi_xfer_result pmcmsptwi_xfer_cmd(
{
enum pmcmsptwi_xfer_result retval;
- if ((cmd->type == MSP_TWI_CMD_WRITE && cmd->write_len == 0) ||
- (cmd->type == MSP_TWI_CMD_READ && cmd->read_len == 0) ||
- (cmd->type == MSP_TWI_CMD_WRITE_READ &&
- (cmd->read_len == 0 || cmd->write_len == 0))) {
- dev_err(&pmcmsptwi_adapter.dev,
- "%s: Cannot transfer less than 1 byte\n",
- __func__);
- return -EINVAL;
- }
-
mutex_lock(&data->lock);
dev_dbg(&pmcmsptwi_adapter.dev,
"Setting address to 0x%04x\n", cmd->addr);
@@ -532,11 +522,6 @@ static int pmcmsptwi_master_xfer(struct i2c_adapter *adap,
cmd.write_data = msg->buf;
}
- if (msg->len == 0) {
- dev_err(&adap->dev, "Zero-byte messages unsupported\n");
- return -EINVAL;
- }
-
cmd.addr = msg->addr;
if (msg->flags & I2C_M_TEN) {
@@ -578,7 +563,7 @@ static u32 pmcmsptwi_i2c_func(struct i2c_adapter *adapter)
}
static const struct i2c_adapter_quirks pmcmsptwi_i2c_quirks = {
- .flags = I2C_AQ_COMB_WRITE_THEN_READ,
+ .flags = I2C_AQ_COMB_WRITE_THEN_READ | I2C_AQ_NO_ZERO_LEN,
.max_write_len = MSP_MAX_BYTES_PER_RW,
.max_read_len = MSP_MAX_BYTES_PER_RW,
.max_comb_1st_msg_len = MSP_MAX_BYTES_PER_RW,
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
new file mode 100644
index 000000000000..36732eb688a4
--- /dev/null
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -0,0 +1,673 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/qcom-geni-se.h>
+#include <linux/spinlock.h>
+
+#define SE_I2C_TX_TRANS_LEN 0x26c
+#define SE_I2C_RX_TRANS_LEN 0x270
+#define SE_I2C_SCL_COUNTERS 0x278
+
+#define SE_I2C_ERR (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |\
+ M_GP_IRQ_1_EN | M_GP_IRQ_3_EN | M_GP_IRQ_4_EN)
+#define SE_I2C_ABORT BIT(1)
+
+/* M_CMD OP codes for I2C */
+#define I2C_WRITE 0x1
+#define I2C_READ 0x2
+#define I2C_WRITE_READ 0x3
+#define I2C_ADDR_ONLY 0x4
+#define I2C_BUS_CLEAR 0x6
+#define I2C_STOP_ON_BUS 0x7
+/* M_CMD params for I2C */
+#define PRE_CMD_DELAY BIT(0)
+#define TIMESTAMP_BEFORE BIT(1)
+#define STOP_STRETCH BIT(2)
+#define TIMESTAMP_AFTER BIT(3)
+#define POST_COMMAND_DELAY BIT(4)
+#define IGNORE_ADD_NACK BIT(6)
+#define READ_FINISHED_WITH_ACK BIT(7)
+#define BYPASS_ADDR_PHASE BIT(8)
+#define SLV_ADDR_MSK GENMASK(15, 9)
+#define SLV_ADDR_SHFT 9
+/* I2C SCL COUNTER fields */
+#define HIGH_COUNTER_MSK GENMASK(29, 20)
+#define HIGH_COUNTER_SHFT 20
+#define LOW_COUNTER_MSK GENMASK(19, 10)
+#define LOW_COUNTER_SHFT 10
+#define CYCLE_COUNTER_MSK GENMASK(9, 0)
+
+enum geni_i2c_err_code {
+ GP_IRQ0,
+ NACK,
+ GP_IRQ2,
+ BUS_PROTO,
+ ARB_LOST,
+ GP_IRQ5,
+ GENI_OVERRUN,
+ GENI_ILLEGAL_CMD,
+ GENI_ABORT_DONE,
+ GENI_TIMEOUT,
+};
+
+#define DM_I2C_CB_ERR ((BIT(NACK) | BIT(BUS_PROTO) | BIT(ARB_LOST)) \
+ << 5)
+
+#define I2C_AUTO_SUSPEND_DELAY 250
+#define KHZ(freq) (1000 * freq)
+#define PACKING_BYTES_PW 4
+
+#define ABORT_TIMEOUT HZ
+#define XFER_TIMEOUT HZ
+#define RST_TIMEOUT HZ
+
+struct geni_i2c_dev {
+ struct geni_se se;
+ u32 tx_wm;
+ int irq;
+ int err;
+ struct i2c_adapter adap;
+ struct completion done;
+ struct i2c_msg *cur;
+ int cur_wr;
+ int cur_rd;
+ spinlock_t lock;
+ u32 clk_freq_out;
+ const struct geni_i2c_clk_fld *clk_fld;
+ int suspended;
+};
+
+struct geni_i2c_err_log {
+ int err;
+ const char *msg;
+};
+
+static const struct geni_i2c_err_log gi2c_log[] = {
+ [GP_IRQ0] = {-EIO, "Unknown I2C err GP_IRQ0"},
+ [NACK] = {-ENXIO, "NACK: slv unresponsive, check its power/reset-ln"},
+ [GP_IRQ2] = {-EIO, "Unknown I2C err GP IRQ2"},
+ [BUS_PROTO] = {-EPROTO, "Bus proto err, noisy/unepxected start/stop"},
+ [ARB_LOST] = {-EAGAIN, "Bus arbitration lost, clock line undriveable"},
+ [GP_IRQ5] = {-EIO, "Unknown I2C err GP IRQ5"},
+ [GENI_OVERRUN] = {-EIO, "Cmd overrun, check GENI cmd-state machine"},
+ [GENI_ILLEGAL_CMD] = {-EIO, "Illegal cmd, check GENI cmd-state machine"},
+ [GENI_ABORT_DONE] = {-ETIMEDOUT, "Abort after timeout successful"},
+ [GENI_TIMEOUT] = {-ETIMEDOUT, "I2C TXN timed out"},
+};
+
+struct geni_i2c_clk_fld {
+ u32 clk_freq_out;
+ u8 clk_div;
+ u8 t_high_cnt;
+ u8 t_low_cnt;
+ u8 t_cycle_cnt;
+};
+
+/*
+ * Hardware uses the underlying formula to calculate time periods of
+ * SCL clock cycle. Firmware uses some additional cycles excluded from the
+ * below formula and it is confirmed that the time periods are within
+ * specification limits.
+ *
+ * time of high period of SCL: t_high = (t_high_cnt * clk_div) / source_clock
+ * time of low period of SCL: t_low = (t_low_cnt * clk_div) / source_clock
+ * time of full period of SCL: t_cycle = (t_cycle_cnt * clk_div) / source_clock
+ * clk_freq_out = t / t_cycle
+ * source_clock = 19.2 MHz
+ */
+static const struct geni_i2c_clk_fld geni_i2c_clk_map[] = {
+ {KHZ(100), 7, 10, 11, 26},
+ {KHZ(400), 2, 5, 12, 24},
+ {KHZ(1000), 1, 3, 9, 18},
+};
+
+static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
+{
+ int i;
+ const struct geni_i2c_clk_fld *itr = geni_i2c_clk_map;
+
+ for (i = 0; i < ARRAY_SIZE(geni_i2c_clk_map); i++, itr++) {
+ if (itr->clk_freq_out == gi2c->clk_freq_out) {
+ gi2c->clk_fld = itr;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c)
+{
+ const struct geni_i2c_clk_fld *itr = gi2c->clk_fld;
+ u32 val;
+
+ writel_relaxed(0, gi2c->se.base + SE_GENI_CLK_SEL);
+
+ val = (itr->clk_div << CLK_DIV_SHFT) | SER_CLK_EN;
+ writel_relaxed(val, gi2c->se.base + GENI_SER_M_CLK_CFG);
+
+ val = itr->t_high_cnt << HIGH_COUNTER_SHFT;
+ val |= itr->t_low_cnt << LOW_COUNTER_SHFT;
+ val |= itr->t_cycle_cnt;
+ writel_relaxed(val, gi2c->se.base + SE_I2C_SCL_COUNTERS);
+}
+
+static void geni_i2c_err_misc(struct geni_i2c_dev *gi2c)
+{
+ u32 m_cmd = readl_relaxed(gi2c->se.base + SE_GENI_M_CMD0);
+ u32 m_stat = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
+ u32 geni_s = readl_relaxed(gi2c->se.base + SE_GENI_STATUS);
+ u32 geni_ios = readl_relaxed(gi2c->se.base + SE_GENI_IOS);
+ u32 dma = readl_relaxed(gi2c->se.base + SE_GENI_DMA_MODE_EN);
+ u32 rx_st, tx_st;
+
+ if (dma) {
+ rx_st = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT);
+ tx_st = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT);
+ } else {
+ rx_st = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFO_STATUS);
+ tx_st = readl_relaxed(gi2c->se.base + SE_GENI_TX_FIFO_STATUS);
+ }
+ dev_dbg(gi2c->se.dev, "DMA:%d tx_stat:0x%x, rx_stat:0x%x, irq-stat:0x%x\n",
+ dma, tx_st, rx_st, m_stat);
+ dev_dbg(gi2c->se.dev, "m_cmd:0x%x, geni_status:0x%x, geni_ios:0x%x\n",
+ m_cmd, geni_s, geni_ios);
+}
+
+static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
+{
+ if (!gi2c->err)
+ gi2c->err = gi2c_log[err].err;
+ if (gi2c->cur)
+ dev_dbg(gi2c->se.dev, "len:%d, slv-addr:0x%x, RD/WR:%d\n",
+ gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags);
+
+ if (err != NACK && err != GENI_ABORT_DONE) {
+ dev_err(gi2c->se.dev, "%s\n", gi2c_log[err].msg);
+ geni_i2c_err_misc(gi2c);
+ }
+}
+
+static irqreturn_t geni_i2c_irq(int irq, void *dev)
+{
+ struct geni_i2c_dev *gi2c = dev;
+ int j;
+ u32 m_stat;
+ u32 rx_st;
+ u32 dm_tx_st;
+ u32 dm_rx_st;
+ u32 dma;
+ struct i2c_msg *cur;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gi2c->lock, flags);
+ m_stat = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
+ rx_st = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFO_STATUS);
+ dm_tx_st = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT);
+ dm_rx_st = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT);
+ dma = readl_relaxed(gi2c->se.base + SE_GENI_DMA_MODE_EN);
+ cur = gi2c->cur;
+
+ if (!cur ||
+ m_stat & (M_CMD_FAILURE_EN | M_CMD_ABORT_EN) ||
+ dm_rx_st & (DM_I2C_CB_ERR)) {
+ if (m_stat & M_GP_IRQ_1_EN)
+ geni_i2c_err(gi2c, NACK);
+ if (m_stat & M_GP_IRQ_3_EN)
+ geni_i2c_err(gi2c, BUS_PROTO);
+ if (m_stat & M_GP_IRQ_4_EN)
+ geni_i2c_err(gi2c, ARB_LOST);
+ if (m_stat & M_CMD_OVERRUN_EN)
+ geni_i2c_err(gi2c, GENI_OVERRUN);
+ if (m_stat & M_ILLEGAL_CMD_EN)
+ geni_i2c_err(gi2c, GENI_ILLEGAL_CMD);
+ if (m_stat & M_CMD_ABORT_EN)
+ geni_i2c_err(gi2c, GENI_ABORT_DONE);
+ if (m_stat & M_GP_IRQ_0_EN)
+ geni_i2c_err(gi2c, GP_IRQ0);
+
+ /* Disable the TX Watermark interrupt to stop TX */
+ if (!dma)
+ writel_relaxed(0, gi2c->se.base +
+ SE_GENI_TX_WATERMARK_REG);
+ goto irqret;
+ }
+
+ if (dma) {
+ dev_dbg(gi2c->se.dev, "i2c dma tx:0x%x, dma rx:0x%x\n",
+ dm_tx_st, dm_rx_st);
+ goto irqret;
+ }
+
+ if (cur->flags & I2C_M_RD &&
+ m_stat & (M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN)) {
+ u32 rxcnt = rx_st & RX_FIFO_WC_MSK;
+
+ for (j = 0; j < rxcnt; j++) {
+ u32 val;
+ int p = 0;
+
+ val = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFOn);
+ while (gi2c->cur_rd < cur->len && p < sizeof(val)) {
+ cur->buf[gi2c->cur_rd++] = val & 0xff;
+ val >>= 8;
+ p++;
+ }
+ if (gi2c->cur_rd == cur->len)
+ break;
+ }
+ } else if (!(cur->flags & I2C_M_RD) &&
+ m_stat & M_TX_FIFO_WATERMARK_EN) {
+ for (j = 0; j < gi2c->tx_wm; j++) {
+ u32 temp;
+ u32 val = 0;
+ int p = 0;
+
+ while (gi2c->cur_wr < cur->len && p < sizeof(val)) {
+ temp = cur->buf[gi2c->cur_wr++];
+ val |= temp << (p * 8);
+ p++;
+ }
+ writel_relaxed(val, gi2c->se.base + SE_GENI_TX_FIFOn);
+ /* TX Complete, Disable the TX Watermark interrupt */
+ if (gi2c->cur_wr == cur->len) {
+ writel_relaxed(0, gi2c->se.base +
+ SE_GENI_TX_WATERMARK_REG);
+ break;
+ }
+ }
+ }
+irqret:
+ if (m_stat)
+ writel_relaxed(m_stat, gi2c->se.base + SE_GENI_M_IRQ_CLEAR);
+
+ if (dma) {
+ if (dm_tx_st)
+ writel_relaxed(dm_tx_st, gi2c->se.base +
+ SE_DMA_TX_IRQ_CLR);
+ if (dm_rx_st)
+ writel_relaxed(dm_rx_st, gi2c->se.base +
+ SE_DMA_RX_IRQ_CLR);
+ }
+ /* if this is err with done-bit not set, handle that through timeout. */
+ if (m_stat & M_CMD_DONE_EN || m_stat & M_CMD_ABORT_EN)
+ complete(&gi2c->done);
+ else if (dm_tx_st & TX_DMA_DONE || dm_tx_st & TX_RESET_DONE)
+ complete(&gi2c->done);
+ else if (dm_rx_st & RX_DMA_DONE || dm_rx_st & RX_RESET_DONE)
+ complete(&gi2c->done);
+
+ spin_unlock_irqrestore(&gi2c->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void geni_i2c_abort_xfer(struct geni_i2c_dev *gi2c)
+{
+ u32 val;
+ unsigned long time_left = ABORT_TIMEOUT;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gi2c->lock, flags);
+ geni_i2c_err(gi2c, GENI_TIMEOUT);
+ gi2c->cur = NULL;
+ geni_se_abort_m_cmd(&gi2c->se);
+ spin_unlock_irqrestore(&gi2c->lock, flags);
+ do {
+ time_left = wait_for_completion_timeout(&gi2c->done, time_left);
+ val = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
+ } while (!(val & M_CMD_ABORT_EN) && time_left);
+
+ if (!(val & M_CMD_ABORT_EN))
+ dev_err(gi2c->se.dev, "Timeout abort_m_cmd\n");
+}
+
+static void geni_i2c_rx_fsm_rst(struct geni_i2c_dev *gi2c)
+{
+ u32 val;
+ unsigned long time_left = RST_TIMEOUT;
+
+ writel_relaxed(1, gi2c->se.base + SE_DMA_RX_FSM_RST);
+ do {
+ time_left = wait_for_completion_timeout(&gi2c->done, time_left);
+ val = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT);
+ } while (!(val & RX_RESET_DONE) && time_left);
+
+ if (!(val & RX_RESET_DONE))
+ dev_err(gi2c->se.dev, "Timeout resetting RX_FSM\n");
+}
+
+static void geni_i2c_tx_fsm_rst(struct geni_i2c_dev *gi2c)
+{
+ u32 val;
+ unsigned long time_left = RST_TIMEOUT;
+
+ writel_relaxed(1, gi2c->se.base + SE_DMA_TX_FSM_RST);
+ do {
+ time_left = wait_for_completion_timeout(&gi2c->done, time_left);
+ val = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT);
+ } while (!(val & TX_RESET_DONE) && time_left);
+
+ if (!(val & TX_RESET_DONE))
+ dev_err(gi2c->se.dev, "Timeout resetting TX_FSM\n");
+}
+
+static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
+ u32 m_param)
+{
+ dma_addr_t rx_dma;
+ enum geni_se_xfer_mode mode;
+ unsigned long time_left = XFER_TIMEOUT;
+
+ gi2c->cur = msg;
+ mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;
+ geni_se_select_mode(&gi2c->se, mode);
+ writel_relaxed(msg->len, gi2c->se.base + SE_I2C_RX_TRANS_LEN);
+ geni_se_setup_m_cmd(&gi2c->se, I2C_READ, m_param);
+ if (mode == GENI_SE_DMA) {
+ int ret;
+
+ ret = geni_se_rx_dma_prep(&gi2c->se, msg->buf, msg->len,
+ &rx_dma);
+ if (ret) {
+ mode = GENI_SE_FIFO;
+ geni_se_select_mode(&gi2c->se, mode);
+ }
+ }
+
+ time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
+ if (!time_left)
+ geni_i2c_abort_xfer(gi2c);
+
+ gi2c->cur_rd = 0;
+ if (mode == GENI_SE_DMA) {
+ if (gi2c->err)
+ geni_i2c_rx_fsm_rst(gi2c);
+ geni_se_rx_dma_unprep(&gi2c->se, rx_dma, msg->len);
+ }
+ return gi2c->err;
+}
+
+static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
+ u32 m_param)
+{
+ dma_addr_t tx_dma;
+ enum geni_se_xfer_mode mode;
+ unsigned long time_left;
+
+ gi2c->cur = msg;
+ mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;
+ geni_se_select_mode(&gi2c->se, mode);
+ writel_relaxed(msg->len, gi2c->se.base + SE_I2C_TX_TRANS_LEN);
+ geni_se_setup_m_cmd(&gi2c->se, I2C_WRITE, m_param);
+ if (mode == GENI_SE_DMA) {
+ int ret;
+
+ ret = geni_se_tx_dma_prep(&gi2c->se, msg->buf, msg->len,
+ &tx_dma);
+ if (ret) {
+ mode = GENI_SE_FIFO;
+ geni_se_select_mode(&gi2c->se, mode);
+ }
+ }
+
+ if (mode == GENI_SE_FIFO) /* Get FIFO IRQ */
+ writel_relaxed(1, gi2c->se.base + SE_GENI_TX_WATERMARK_REG);
+
+ time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
+ if (!time_left)
+ geni_i2c_abort_xfer(gi2c);
+
+ gi2c->cur_wr = 0;
+ if (mode == GENI_SE_DMA) {
+ if (gi2c->err)
+ geni_i2c_tx_fsm_rst(gi2c);
+ geni_se_tx_dma_unprep(&gi2c->se, tx_dma, msg->len);
+ }
+ return gi2c->err;
+}
+
+static int geni_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msgs[],
+ int num)
+{
+ struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
+ int i, ret;
+
+ gi2c->err = 0;
+ reinit_completion(&gi2c->done);
+ ret = pm_runtime_get_sync(gi2c->se.dev);
+ if (ret < 0) {
+ dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret);
+ pm_runtime_put_noidle(gi2c->se.dev);
+ /* Set device in suspended since resume failed */
+ pm_runtime_set_suspended(gi2c->se.dev);
+ return ret;
+ }
+
+ qcom_geni_i2c_conf(gi2c);
+ for (i = 0; i < num; i++) {
+ u32 m_param = i < (num - 1) ? STOP_STRETCH : 0;
+
+ m_param |= ((msgs[i].addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK);
+
+ if (msgs[i].flags & I2C_M_RD)
+ ret = geni_i2c_rx_one_msg(gi2c, &msgs[i], m_param);
+ else
+ ret = geni_i2c_tx_one_msg(gi2c, &msgs[i], m_param);
+
+ if (ret)
+ break;
+ }
+ if (ret == 0)
+ ret = num;
+
+ pm_runtime_mark_last_busy(gi2c->se.dev);
+ pm_runtime_put_autosuspend(gi2c->se.dev);
+ gi2c->cur = NULL;
+ gi2c->err = 0;
+ return ret;
+}
+
+static u32 geni_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+static const struct i2c_algorithm geni_i2c_algo = {
+ .master_xfer = geni_i2c_xfer,
+ .functionality = geni_i2c_func,
+};
+
+static int geni_i2c_probe(struct platform_device *pdev)
+{
+ struct geni_i2c_dev *gi2c;
+ struct resource *res;
+ u32 proto, tx_depth;
+ int ret;
+
+ gi2c = devm_kzalloc(&pdev->dev, sizeof(*gi2c), GFP_KERNEL);
+ if (!gi2c)
+ return -ENOMEM;
+
+ gi2c->se.dev = &pdev->dev;
+ gi2c->se.wrapper = dev_get_drvdata(pdev->dev.parent);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ gi2c->se.base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(gi2c->se.base))
+ return PTR_ERR(gi2c->se.base);
+
+ gi2c->se.clk = devm_clk_get(&pdev->dev, "se");
+ if (IS_ERR(gi2c->se.clk)) {
+ ret = PTR_ERR(gi2c->se.clk);
+ dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
+ return ret;
+ }
+
+ ret = device_property_read_u32(&pdev->dev, "clock-frequency",
+ &gi2c->clk_freq_out);
+ if (ret) {
+ dev_info(&pdev->dev,
+ "Bus frequency not specified, default to 100kHz.\n");
+ gi2c->clk_freq_out = KHZ(100);
+ }
+
+ gi2c->irq = platform_get_irq(pdev, 0);
+ if (gi2c->irq < 0) {
+ dev_err(&pdev->dev, "IRQ error for i2c-geni\n");
+ return gi2c->irq;
+ }
+
+ ret = geni_i2c_clk_map_idx(gi2c);
+ if (ret) {
+ dev_err(&pdev->dev, "Invalid clk frequency %d Hz: %d\n",
+ gi2c->clk_freq_out, ret);
+ return ret;
+ }
+
+ gi2c->adap.algo = &geni_i2c_algo;
+ init_completion(&gi2c->done);
+ spin_lock_init(&gi2c->lock);
+ platform_set_drvdata(pdev, gi2c);
+ ret = devm_request_irq(&pdev->dev, gi2c->irq, geni_i2c_irq,
+ IRQF_TRIGGER_HIGH, "i2c_geni", gi2c);
+ if (ret) {
+ dev_err(&pdev->dev, "Request_irq failed:%d: err:%d\n",
+ gi2c->irq, ret);
+ return ret;
+ }
+ /* Disable the interrupt so that the system can enter low-power mode */
+ disable_irq(gi2c->irq);
+ i2c_set_adapdata(&gi2c->adap, gi2c);
+ gi2c->adap.dev.parent = &pdev->dev;
+ gi2c->adap.dev.of_node = pdev->dev.of_node;
+ strlcpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));
+
+ ret = geni_se_resources_on(&gi2c->se);
+ if (ret) {
+ dev_err(&pdev->dev, "Error turning on resources %d\n", ret);
+ return ret;
+ }
+ proto = geni_se_read_proto(&gi2c->se);
+ tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se);
+ if (proto != GENI_SE_I2C) {
+ dev_err(&pdev->dev, "Invalid proto %d\n", proto);
+ geni_se_resources_off(&gi2c->se);
+ return -ENXIO;
+ }
+ gi2c->tx_wm = tx_depth - 1;
+ geni_se_init(&gi2c->se, gi2c->tx_wm, tx_depth);
+ geni_se_config_packing(&gi2c->se, BITS_PER_BYTE, PACKING_BYTES_PW,
+ true, true, true);
+ ret = geni_se_resources_off(&gi2c->se);
+ if (ret) {
+ dev_err(&pdev->dev, "Error turning off resources %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(&pdev->dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth);
+
+ ret = i2c_add_adapter(&gi2c->adap);
+ if (ret) {
+ dev_err(&pdev->dev, "Error adding i2c adapter %d\n", ret);
+ return ret;
+ }
+
+ gi2c->suspended = 1;
+ pm_runtime_set_suspended(gi2c->se.dev);
+ pm_runtime_set_autosuspend_delay(gi2c->se.dev, I2C_AUTO_SUSPEND_DELAY);
+ pm_runtime_use_autosuspend(gi2c->se.dev);
+ pm_runtime_enable(gi2c->se.dev);
+
+ return 0;
+}
+
+static int geni_i2c_remove(struct platform_device *pdev)
+{
+ struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(gi2c->se.dev);
+ i2c_del_adapter(&gi2c->adap);
+ return 0;
+}
+
+static int __maybe_unused geni_i2c_runtime_suspend(struct device *dev)
+{
+ int ret;
+ struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
+
+ disable_irq(gi2c->irq);
+ ret = geni_se_resources_off(&gi2c->se);
+ if (ret) {
+ enable_irq(gi2c->irq);
+ return ret;
+
+ } else {
+ gi2c->suspended = 1;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused geni_i2c_runtime_resume(struct device *dev)
+{
+ int ret;
+ struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
+
+ ret = geni_se_resources_on(&gi2c->se);
+ if (ret)
+ return ret;
+
+ enable_irq(gi2c->irq);
+ gi2c->suspended = 0;
+ return 0;
+}
+
+static int __maybe_unused geni_i2c_suspend_noirq(struct device *dev)
+{
+ struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
+
+ if (!gi2c->suspended) {
+ geni_i2c_runtime_suspend(dev);
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_enable(dev);
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops geni_i2c_pm_ops = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(geni_i2c_suspend_noirq, NULL)
+ SET_RUNTIME_PM_OPS(geni_i2c_runtime_suspend, geni_i2c_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id geni_i2c_dt_match[] = {
+ { .compatible = "qcom,geni-i2c" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, geni_i2c_dt_match);
+
+static struct platform_driver geni_i2c_driver = {
+ .probe = geni_i2c_probe,
+ .remove = geni_i2c_remove,
+ .driver = {
+ .name = "geni_i2c",
+ .pm = &geni_i2c_pm_ops,
+ .of_match_table = geni_i2c_dt_match,
+ },
+};
+
+module_platform_driver(geni_i2c_driver);
+
+MODULE_DESCRIPTION("I2C Controller Driver for GENI based QUP cores");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
index 3c1c817f6968..43ad933df0f0 100644
--- a/drivers/i2c/busses/i2c-rcar.c
+++ b/drivers/i2c/busses/i2c-rcar.c
@@ -19,6 +19,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
+#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
@@ -112,9 +113,10 @@
#define ID_ARBLOST (1 << 3)
#define ID_NACK (1 << 4)
/* persistent flags */
-#define ID_P_NO_RXDMA (1 << 30) /* HW forbids RXDMA sometimes */
-#define ID_P_PM_BLOCKED (1 << 31)
-#define ID_P_MASK (ID_P_PM_BLOCKED | ID_P_NO_RXDMA)
+#define ID_P_REP_AFTER_RD BIT(29)
+#define ID_P_NO_RXDMA BIT(30) /* HW forbids RXDMA sometimes */
+#define ID_P_PM_BLOCKED BIT(31)
+#define ID_P_MASK GENMASK(31, 29)
enum rcar_i2c_type {
I2C_RCAR_GEN1,
@@ -183,8 +185,6 @@ static void rcar_i2c_set_scl(struct i2c_adapter *adap, int val)
rcar_i2c_write(priv, ICMCR, priv->recovery_icmcr);
};
-/* No get_sda, because the HW only reports its bus free logic, not SDA itself */
-
static void rcar_i2c_set_sda(struct i2c_adapter *adap, int val)
{
struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
@@ -197,10 +197,19 @@ static void rcar_i2c_set_sda(struct i2c_adapter *adap, int val)
rcar_i2c_write(priv, ICMCR, priv->recovery_icmcr);
};
+static int rcar_i2c_get_bus_free(struct i2c_adapter *adap)
+{
+ struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
+
+ return !(rcar_i2c_read(priv, ICMCR) & FSDA);
+
+};
+
static struct i2c_bus_recovery_info rcar_i2c_bri = {
.get_scl = rcar_i2c_get_scl,
.set_scl = rcar_i2c_set_scl,
.set_sda = rcar_i2c_set_sda,
+ .get_bus_free = rcar_i2c_get_bus_free,
.recover_bus = i2c_generic_scl_recovery,
};
static void rcar_i2c_init(struct rcar_i2c_priv *priv)
@@ -215,7 +224,7 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv)
static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
{
- int i, ret;
+ int i;
for (i = 0; i < LOOP_TIMEOUT; i++) {
/* make sure that bus is not busy */
@@ -226,13 +235,7 @@ static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
/* Waiting did not help, try to recover */
priv->recovery_icmcr = MDBS | OBPC | FSDA | FSCL;
- ret = i2c_recover_bus(&priv->adap);
-
- /* No failure when recovering, so check bus busy bit again */
- if (ret == 0)
- ret = (rcar_i2c_read(priv, ICMCR) & FSDA) ? -EBUSY : 0;
-
- return ret;
+ return i2c_recover_bus(&priv->adap);
}
static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv, struct i2c_timings *t)
@@ -343,7 +346,10 @@ static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv)
rcar_i2c_write(priv, ICMSR, 0);
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
} else {
- rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
+ if (priv->flags & ID_P_REP_AFTER_RD)
+ priv->flags &= ~ID_P_REP_AFTER_RD;
+ else
+ rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
rcar_i2c_write(priv, ICMSR, 0);
}
rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND);
@@ -548,15 +554,15 @@ static void rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
priv->pos++;
}
- /*
- * If next received data is the _LAST_, go to STOP phase. Might be
- * overwritten by REP START when setting up a new msg. Not elegant
- * but the only stable sequence for REP START I have found so far.
- * If you want to change this code, make sure sending one transfer with
- * four messages (WR-RD-WR-RD) works!
- */
- if (priv->pos + 1 >= msg->len)
- rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP);
+ /* If next received data is the _LAST_, go to new phase. */
+ if (priv->pos + 1 == msg->len) {
+ if (priv->flags & ID_LAST_MSG) {
+ rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP);
+ } else {
+ rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
+ priv->flags |= ID_P_REP_AFTER_RD;
+ }
+ }
if (priv->pos == msg->len && !(priv->flags & ID_LAST_MSG))
rcar_i2c_next_msg(priv);
@@ -624,9 +630,11 @@ static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
struct rcar_i2c_priv *priv = ptr;
u32 msr, val;
- /* Clear START or STOP as soon as we can */
- val = rcar_i2c_read(priv, ICMCR);
- rcar_i2c_write(priv, ICMCR, val & RCAR_BUS_MASK_DATA);
+ /* Clear START or STOP immediately, except for REPSTART after read */
+ if (likely(!(priv->flags & ID_P_REP_AFTER_RD))) {
+ val = rcar_i2c_read(priv, ICMCR);
+ rcar_i2c_write(priv, ICMCR, val & RCAR_BUS_MASK_DATA);
+ }
msr = rcar_i2c_read(priv, ICMSR);
@@ -795,14 +803,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
if (ret < 0)
goto out;
- for (i = 0; i < num; i++) {
- /* This HW can't send STOP after address phase */
- if (msgs[i].len == 0) {
- ret = -EOPNOTSUPP;
- goto out;
- }
+ for (i = 0; i < num; i++)
rcar_i2c_request_dma(priv, msgs + i);
- }
/* init first message */
priv->msg = msgs;
@@ -889,6 +891,10 @@ static const struct i2c_algorithm rcar_i2c_algo = {
.unreg_slave = rcar_unreg_slave,
};
+static const struct i2c_adapter_quirks rcar_i2c_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN,
+};
+
static const struct of_device_id rcar_i2c_dt_ids[] = {
{ .compatible = "renesas,i2c-r8a7778", .data = (void *)I2C_RCAR_GEN1 },
{ .compatible = "renesas,i2c-r8a7779", .data = (void *)I2C_RCAR_GEN1 },
@@ -942,6 +948,7 @@ static int rcar_i2c_probe(struct platform_device *pdev)
adap->dev.parent = dev;
adap->dev.of_node = dev->of_node;
adap->bus_recovery_info = &rcar_i2c_bri;
+ adap->quirks = &rcar_i2c_quirks;
i2c_set_adapdata(adap, priv);
strlcpy(adap->name, pdev->name, sizeof(adap->name));
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index 9fe2b6951895..2f2e28d60ef5 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -919,9 +919,9 @@ static int s3c24xx_i2c_cpufreq_transition(struct notifier_block *nb,
if ((val == CPUFREQ_POSTCHANGE && delta_f < 0) ||
(val == CPUFREQ_PRECHANGE && delta_f > 0)) {
- i2c_lock_adapter(&i2c->adap);
+ i2c_lock_bus(&i2c->adap, I2C_LOCK_ROOT_ADAPTER);
ret = s3c24xx_i2c_clockrate(i2c, &got);
- i2c_unlock_adapter(&i2c->adap);
+ i2c_unlock_bus(&i2c->adap, I2C_LOCK_ROOT_ADAPTER);
if (ret < 0)
dev_err(i2c->dev, "cannot find frequency (%d)\n", ret);
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index 5fda4188a9e5..9c7f6f8ceb22 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -613,11 +613,6 @@ static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd)
static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg,
bool do_init)
{
- if (usr_msg->len == 0 && (usr_msg->flags & I2C_M_RD)) {
- dev_err(pd->dev, "Unsupported zero length i2c read\n");
- return -EOPNOTSUPP;
- }
-
if (do_init) {
/* Initialize channel registers */
iic_wr(pd, ICCR, ICCR_SCP);
@@ -758,6 +753,10 @@ static const struct i2c_algorithm sh_mobile_i2c_algorithm = {
.master_xfer = sh_mobile_i2c_xfer,
};
+static const struct i2c_adapter_quirks sh_mobile_i2c_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN_READ,
+};
+
/*
* r8a7740 chip has lasting errata on I2C I/O pad reset.
* this is work-around for it.
@@ -925,6 +924,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
adap->owner = THIS_MODULE;
adap->algo = &sh_mobile_i2c_algorithm;
+ adap->quirks = &sh_mobile_i2c_quirks;
adap->dev.parent = &dev->dev;
adap->retries = 5;
adap->nr = dev->id;
diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c
index 4053259bccb8..a94e724f51dc 100644
--- a/drivers/i2c/busses/i2c-sprd.c
+++ b/drivers/i2c/busses/i2c-sprd.c
@@ -590,9 +590,9 @@ static int __maybe_unused sprd_i2c_suspend_noirq(struct device *pdev)
{
struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
- i2c_lock_adapter(&i2c_dev->adap);
+ i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
i2c_dev->is_suspended = true;
- i2c_unlock_adapter(&i2c_dev->adap);
+ i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
return pm_runtime_force_suspend(pdev);
}
@@ -601,9 +601,9 @@ static int __maybe_unused sprd_i2c_resume_noirq(struct device *pdev)
{
struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
- i2c_lock_adapter(&i2c_dev->adap);
+ i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
i2c_dev->is_suspended = false;
- i2c_unlock_adapter(&i2c_dev->adap);
+ i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
return pm_runtime_force_resume(pdev);
}
diff --git a/drivers/i2c/busses/i2c-stu300.c b/drivers/i2c/busses/i2c-stu300.c
index fce52bdab2b7..5503fa171df0 100644
--- a/drivers/i2c/busses/i2c-stu300.c
+++ b/drivers/i2c/busses/i2c-stu300.c
@@ -673,12 +673,6 @@ static int stu300_xfer_msg(struct i2c_adapter *adap,
msg->addr, msg->len, msg->flags, stop);
}
- /* Zero-length messages are not supported by this hardware */
- if (msg->len == 0) {
- ret = -EINVAL;
- goto exit_disable;
- }
-
/*
* For some reason, sending the address sometimes fails when running
* on the 13 MHz clock. No interrupt arrives. This is a work around,
@@ -863,6 +857,10 @@ static const struct i2c_algorithm stu300_algo = {
.functionality = stu300_func,
};
+static const struct i2c_adapter_quirks stu300_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN,
+};
+
static int stu300_probe(struct platform_device *pdev)
{
struct stu300_dev *dev;
@@ -920,6 +918,8 @@ static int stu300_probe(struct platform_device *pdev)
adap->algo = &stu300_algo;
adap->dev.parent = &pdev->dev;
adap->dev.of_node = pdev->dev.of_node;
+ adap->quirks = &stu300_quirks;
+
i2c_set_adapdata(adap, dev);
/* i2c device drivers may be active on return from add_adapter() */
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 797def5319f1..60c8561fbe65 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -115,6 +115,18 @@
#define I2C_CONFIG_LOAD_TIMEOUT 1000000
+#define I2C_MST_FIFO_CONTROL 0x0b4
+#define I2C_MST_FIFO_CONTROL_RX_FLUSH BIT(0)
+#define I2C_MST_FIFO_CONTROL_TX_FLUSH BIT(1)
+#define I2C_MST_FIFO_CONTROL_RX_TRIG(x) (((x) - 1) << 4)
+#define I2C_MST_FIFO_CONTROL_TX_TRIG(x) (((x) - 1) << 16)
+
+#define I2C_MST_FIFO_STATUS 0x0b8
+#define I2C_MST_FIFO_STATUS_RX_MASK 0xff
+#define I2C_MST_FIFO_STATUS_RX_SHIFT 0
+#define I2C_MST_FIFO_STATUS_TX_MASK 0xff0000
+#define I2C_MST_FIFO_STATUS_TX_SHIFT 16
+
/*
* msg_end_type: The bus control which need to be send at end of transfer.
* @MSG_END_STOP: Send stop pulse at end of transfer.
@@ -154,6 +166,7 @@ struct tegra_i2c_hw_feature {
u16 clk_divisor_fast_plus_mode;
bool has_multi_master_mode;
bool has_slcg_override_reg;
+ bool has_mst_fifo;
};
/**
@@ -266,13 +279,24 @@ static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
{
unsigned long timeout = jiffies + HZ;
- u32 val = i2c_readl(i2c_dev, I2C_FIFO_CONTROL);
+ unsigned int offset;
+ u32 mask, val;
+
+ if (i2c_dev->hw->has_mst_fifo) {
+ mask = I2C_MST_FIFO_CONTROL_TX_FLUSH |
+ I2C_MST_FIFO_CONTROL_RX_FLUSH;
+ offset = I2C_MST_FIFO_CONTROL;
+ } else {
+ mask = I2C_FIFO_CONTROL_TX_FLUSH |
+ I2C_FIFO_CONTROL_RX_FLUSH;
+ offset = I2C_FIFO_CONTROL;
+ }
- val |= I2C_FIFO_CONTROL_TX_FLUSH | I2C_FIFO_CONTROL_RX_FLUSH;
- i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
+ val = i2c_readl(i2c_dev, offset);
+ val |= mask;
+ i2c_writel(i2c_dev, val, offset);
- while (i2c_readl(i2c_dev, I2C_FIFO_CONTROL) &
- (I2C_FIFO_CONTROL_TX_FLUSH | I2C_FIFO_CONTROL_RX_FLUSH)) {
+ while (i2c_readl(i2c_dev, offset) & mask) {
if (time_after(jiffies, timeout)) {
dev_warn(i2c_dev->dev, "timeout waiting for fifo flush\n");
return -ETIMEDOUT;
@@ -290,9 +314,15 @@ static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev)
size_t buf_remaining = i2c_dev->msg_buf_remaining;
int words_to_transfer;
- val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
- rx_fifo_avail = (val & I2C_FIFO_STATUS_RX_MASK) >>
- I2C_FIFO_STATUS_RX_SHIFT;
+ if (i2c_dev->hw->has_mst_fifo) {
+ val = i2c_readl(i2c_dev, I2C_MST_FIFO_STATUS);
+ rx_fifo_avail = (val & I2C_MST_FIFO_STATUS_RX_MASK) >>
+ I2C_MST_FIFO_STATUS_RX_SHIFT;
+ } else {
+ val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
+ rx_fifo_avail = (val & I2C_FIFO_STATUS_RX_MASK) >>
+ I2C_FIFO_STATUS_RX_SHIFT;
+ }
/* Rounds down to not include partial word at the end of buf */
words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD;
@@ -321,6 +351,7 @@ static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev)
BUG_ON(rx_fifo_avail > 0 && buf_remaining > 0);
i2c_dev->msg_buf_remaining = buf_remaining;
i2c_dev->msg_buf = buf;
+
return 0;
}
@@ -332,9 +363,15 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev)
size_t buf_remaining = i2c_dev->msg_buf_remaining;
int words_to_transfer;
- val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
- tx_fifo_avail = (val & I2C_FIFO_STATUS_TX_MASK) >>
- I2C_FIFO_STATUS_TX_SHIFT;
+ if (i2c_dev->hw->has_mst_fifo) {
+ val = i2c_readl(i2c_dev, I2C_MST_FIFO_STATUS);
+ tx_fifo_avail = (val & I2C_MST_FIFO_STATUS_TX_MASK) >>
+ I2C_MST_FIFO_STATUS_TX_SHIFT;
+ } else {
+ val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
+ tx_fifo_avail = (val & I2C_FIFO_STATUS_TX_MASK) >>
+ I2C_FIFO_STATUS_TX_SHIFT;
+ }
/* Rounds down to not include partial word at the end of buf */
words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD;
@@ -516,9 +553,15 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2);
}
- val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT |
- 0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
- i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
+ if (i2c_dev->hw->has_mst_fifo) {
+ val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) |
+ I2C_MST_FIFO_CONTROL_RX_TRIG(1);
+ i2c_writel(i2c_dev, val, I2C_MST_FIFO_CONTROL);
+ } else {
+ val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT |
+ 0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
+ i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
+ }
err = tegra_i2c_flush_fifos(i2c_dev);
if (err)
@@ -802,6 +845,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
.has_config_load_reg = false,
.has_multi_master_mode = false,
.has_slcg_override_reg = false,
+ .has_mst_fifo = false,
};
static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
@@ -814,6 +858,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
.has_config_load_reg = false,
.has_multi_master_mode = false,
.has_slcg_override_reg = false,
+ .has_mst_fifo = false,
};
static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
@@ -826,6 +871,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
.has_config_load_reg = false,
.has_multi_master_mode = false,
.has_slcg_override_reg = false,
+ .has_mst_fifo = false,
};
static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
@@ -838,6 +884,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
.has_config_load_reg = true,
.has_multi_master_mode = false,
.has_slcg_override_reg = true,
+ .has_mst_fifo = false,
};
static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
@@ -850,10 +897,25 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
.has_config_load_reg = true,
.has_multi_master_mode = true,
.has_slcg_override_reg = true,
+ .has_mst_fifo = false,
+};
+
+static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
+ .has_continue_xfer_support = true,
+ .has_per_pkt_xfer_complete_irq = true,
+ .has_single_clk_source = true,
+ .clk_divisor_hs_mode = 1,
+ .clk_divisor_std_fast_mode = 0x19,
+ .clk_divisor_fast_plus_mode = 0x10,
+ .has_config_load_reg = true,
+ .has_multi_master_mode = true,
+ .has_slcg_override_reg = true,
+ .has_mst_fifo = true,
};
/* Match table for of_platform binding */
static const struct of_device_id tegra_i2c_of_match[] = {
+ { .compatible = "nvidia,tegra194-i2c", .data = &tegra194_i2c_hw, },
{ .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, },
{ .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, },
{ .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, },
diff --git a/drivers/i2c/busses/i2c-xlr.c b/drivers/i2c/busses/i2c-xlr.c
index 484bfa15d58e..34cd4b308540 100644
--- a/drivers/i2c/busses/i2c-xlr.c
+++ b/drivers/i2c/busses/i2c-xlr.c
@@ -173,9 +173,6 @@ static int xlr_i2c_tx(struct xlr_i2c_private *priv, u16 len,
u8 offset;
u32 xfer;
- if (!len)
- return -EOPNOTSUPP;
-
offset = buf[0];
xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset);
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
@@ -241,9 +238,6 @@ static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 len, u8 *buf, u16 addr)
unsigned long timeout, stoptime, checktime;
int nbytes, timedout;
- if (!len)
- return -EOPNOTSUPP;
-
xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
XLR_I2C_CFG_NOADDR | priv->cfg->cfg_extra);
xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
@@ -340,6 +334,10 @@ static const struct i2c_algorithm xlr_i2c_algo = {
.functionality = xlr_func,
};
+static const struct i2c_adapter_quirks xlr_i2c_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN,
+};
+
static const struct xlr_i2c_config xlr_i2c_config_default = {
.status_busy = XLR_I2C_BUS_BUSY,
.cfg_extra = 0,
@@ -427,6 +425,7 @@ static int xlr_i2c_probe(struct platform_device *pdev)
priv->adap.owner = THIS_MODULE;
priv->adap.algo_data = priv;
priv->adap.algo = &xlr_i2c_algo;
+ priv->adap.quirks = &xlr_i2c_quirks;
priv->adap.nr = pdev->id;
priv->adap.class = I2C_CLASS_HWMON;
snprintf(priv->adap.name, sizeof(priv->adap.name), "xlr-i2c");
diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c
index 7c3b4740b94b..32affd3fa8bd 100644
--- a/drivers/i2c/i2c-core-acpi.c
+++ b/drivers/i2c/i2c-core-acpi.c
@@ -453,8 +453,12 @@ static int acpi_gsb_i2c_read_bytes(struct i2c_client *client,
else
dev_err(&client->adapter->dev, "i2c read %d bytes from client@%#x starting at reg %#x failed, error: %d\n",
data_len, client->addr, cmd, ret);
- } else {
+ /* 2 transfers must have completed successfully */
+ } else if (ret == 2) {
memcpy(data, buffer, data_len);
+ ret = 0;
+ } else {
+ ret = -EIO;
}
kfree(buffer);
@@ -482,11 +486,16 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client,
msgs[0].buf = buffer;
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
- if (ret < 0)
- dev_err(&client->adapter->dev, "i2c write failed\n");
kfree(buffer);
- return ret;
+
+ if (ret < 0) {
+ dev_err(&client->adapter->dev, "i2c write failed: %d\n", ret);
+ return ret;
+ }
+
+ /* 1 transfer must have completed successfully */
+ return (ret == 1) ? 0 : -EIO;
}
static acpi_status
@@ -590,8 +599,6 @@ i2c_acpi_space_handler(u32 function, acpi_physical_address command,
if (action == ACPI_READ) {
status = acpi_gsb_i2c_read_bytes(client, command,
gsb->data, info->access_length);
- if (status > 0)
- status = 0;
} else {
status = acpi_gsb_i2c_write_bytes(client, command,
gsb->data, info->access_length);
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index 15c95aaa484c..5a937109a289 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -158,6 +158,22 @@ static void set_sda_gpio_value(struct i2c_adapter *adap, int val)
gpiod_set_value_cansleep(adap->bus_recovery_info->sda_gpiod, val);
}
+static int i2c_generic_bus_free(struct i2c_adapter *adap)
+{
+ struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
+ int ret = -EOPNOTSUPP;
+
+ if (bri->get_bus_free)
+ ret = bri->get_bus_free(adap);
+ else if (bri->get_sda)
+ ret = bri->get_sda(adap);
+
+ if (ret < 0)
+ return ret;
+
+ return ret ? 0 : -EBUSY;
+}
+
/*
* We are generating clock pulses. ndelay() determines durating of clk pulses.
* We will generate clock with rate 100 KHz and so duration of both clock levels
@@ -169,21 +185,28 @@ static void set_sda_gpio_value(struct i2c_adapter *adap, int val)
int i2c_generic_scl_recovery(struct i2c_adapter *adap)
{
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
- int i = 0, val = 1, ret = 0;
+ int i = 0, scl = 1, ret;
if (bri->prepare_recovery)
bri->prepare_recovery(adap);
- bri->set_scl(adap, val);
+ /*
+ * If we can set SDA, we will always create a STOP to ensure additional
+ * pulses will do no harm. This is achieved by letting SDA follow SCL
+ * half a cycle later. Check the 'incomplete_write_byte' fault injector
+ * for details.
+ */
+ bri->set_scl(adap, scl);
+ ndelay(RECOVERY_NDELAY / 2);
if (bri->set_sda)
- bri->set_sda(adap, 1);
- ndelay(RECOVERY_NDELAY);
+ bri->set_sda(adap, scl);
+ ndelay(RECOVERY_NDELAY / 2);
/*
* By this time SCL is high, as we need to give 9 falling-rising edges
*/
while (i++ < RECOVERY_CLK_CNT * 2) {
- if (val) {
+ if (scl) {
/* SCL shouldn't be low here */
if (!bri->get_scl(adap)) {
dev_err(&adap->dev,
@@ -191,41 +214,27 @@ int i2c_generic_scl_recovery(struct i2c_adapter *adap)
ret = -EBUSY;
break;
}
- /* Break if SDA is high */
- if (bri->get_sda && bri->get_sda(adap))
- break;
}
- val = !val;
- bri->set_scl(adap, val);
-
- /*
- * If we can set SDA, we will always create STOP here to ensure
- * the additional pulses will do no harm. This is achieved by
- * letting SDA follow SCL half a cycle later.
- */
+ scl = !scl;
+ bri->set_scl(adap, scl);
+ /* Creating STOP again, see above */
ndelay(RECOVERY_NDELAY / 2);
if (bri->set_sda)
- bri->set_sda(adap, val);
+ bri->set_sda(adap, scl);
ndelay(RECOVERY_NDELAY / 2);
- }
- /* check if recovery actually succeeded */
- if (bri->get_sda && !bri->get_sda(adap))
- ret = -EBUSY;
-
- /* If all went well, send STOP for a sane bus state. */
- if (ret == 0 && bri->set_sda) {
- bri->set_scl(adap, 0);
- ndelay(RECOVERY_NDELAY / 2);
- bri->set_sda(adap, 0);
- ndelay(RECOVERY_NDELAY / 2);
- bri->set_scl(adap, 1);
- ndelay(RECOVERY_NDELAY / 2);
- bri->set_sda(adap, 1);
- ndelay(RECOVERY_NDELAY / 2);
+ if (scl) {
+ ret = i2c_generic_bus_free(adap);
+ if (ret == 0)
+ break;
+ }
}
+ /* If we can't check bus status, assume recovery worked */
+ if (ret == -EOPNOTSUPP)
+ ret = 0;
+
if (bri->unprepare_recovery)
bri->unprepare_recovery(adap);
@@ -274,6 +283,10 @@ static void i2c_init_recovery(struct i2c_adapter *adap)
err_str = "no {get|set}_scl() found";
goto err;
}
+ if (!bri->set_sda && !bri->get_sda) {
+ err_str = "either get_sda() or set_sda() needed";
+ goto err;
+ }
}
return;
@@ -1563,6 +1576,8 @@ void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_de
ret = device_property_read_u32(dev, "i2c-sda-falling-time-ns", &t->sda_fall_ns);
if (ret && use_defaults)
t->sda_fall_ns = t->scl_fall_ns;
+
+ device_property_read_u32(dev, "i2c-sda-hold-time-ns", &t->sda_hold_ns);
}
EXPORT_SYMBOL_GPL(i2c_parse_fw_timings);
@@ -1826,9 +1841,15 @@ static int i2c_check_for_quirks(struct i2c_adapter *adap, struct i2c_msg *msgs,
if (msgs[i].flags & I2C_M_RD) {
if (do_len_check && i2c_quirk_exceeded(len, q->max_read_len))
return i2c_quirk_error(adap, &msgs[i], "msg too long");
+
+ if (q->flags & I2C_AQ_NO_ZERO_LEN_READ && len == 0)
+ return i2c_quirk_error(adap, &msgs[i], "no zero length");
} else {
if (do_len_check && i2c_quirk_exceeded(len, q->max_write_len))
return i2c_quirk_error(adap, &msgs[i], "msg too long");
+
+ if (q->flags & I2C_AQ_NO_ZERO_LEN_WRITE && len == 0)
+ return i2c_quirk_error(adap, &msgs[i], "no zero length");
}
}
diff --git a/drivers/i2c/i2c-core-slave.c b/drivers/i2c/i2c-core-slave.c
index 4a78c65e9971..47a9f70a24a9 100644
--- a/drivers/i2c/i2c-core-slave.c
+++ b/drivers/i2c/i2c-core-slave.c
@@ -47,9 +47,9 @@ int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb)
client->slave_cb = slave_cb;
- i2c_lock_adapter(client->adapter);
+ i2c_lock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER);
ret = client->adapter->algo->reg_slave(client);
- i2c_unlock_adapter(client->adapter);
+ i2c_unlock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER);
if (ret) {
client->slave_cb = NULL;
@@ -69,9 +69,9 @@ int i2c_slave_unregister(struct i2c_client *client)
return -EOPNOTSUPP;
}
- i2c_lock_adapter(client->adapter);
+ i2c_lock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER);
ret = client->adapter->algo->unreg_slave(client);
- i2c_unlock_adapter(client->adapter);
+ i2c_unlock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER);
if (ret == 0)
client->slave_cb = NULL;
diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
index 29646aa6132e..f330690b4125 100644
--- a/drivers/i2c/i2c-mux.c
+++ b/drivers/i2c/i2c-mux.c
@@ -87,8 +87,8 @@ static int __i2c_mux_smbus_xfer(struct i2c_adapter *adap,
ret = muxc->select(muxc, priv->chan_id);
if (ret >= 0)
- ret = parent->algo->smbus_xfer(parent, addr, flags,
- read_write, command, size, data);
+ ret = __i2c_smbus_xfer(parent, addr, flags,
+ read_write, command, size, data);
if (muxc->deselect)
muxc->deselect(muxc, priv->chan_id);
diff --git a/drivers/i2c/muxes/i2c-mux-mlxcpld.c b/drivers/i2c/muxes/i2c-mux-mlxcpld.c
index 12ad8d65faf6..f2bf3e57ed67 100644
--- a/drivers/i2c/muxes/i2c-mux-mlxcpld.c
+++ b/drivers/i2c/muxes/i2c-mux-mlxcpld.c
@@ -94,31 +94,11 @@ static int mlxcpld_mux_reg_write(struct i2c_adapter *adap,
struct i2c_client *client, u8 val)
{
struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&client->dev);
- int ret = -ENODEV;
-
- if (adap->algo->master_xfer) {
- struct i2c_msg msg;
- u8 msgbuf[] = {pdata->sel_reg_addr, val};
-
- msg.addr = client->addr;
- msg.flags = 0;
- msg.len = 2;
- msg.buf = msgbuf;
- ret = __i2c_transfer(adap, &msg, 1);
-
- if (ret >= 0 && ret != 1)
- ret = -EREMOTEIO;
- } else if (adap->algo->smbus_xfer) {
- union i2c_smbus_data data;
-
- data.byte = val;
- ret = adap->algo->smbus_xfer(adap, client->addr,
- client->flags, I2C_SMBUS_WRITE,
- pdata->sel_reg_addr,
- I2C_SMBUS_BYTE_DATA, &data);
- }
+ union i2c_smbus_data data = { .byte = val };
- return ret;
+ return __i2c_smbus_xfer(adap, client->addr, client->flags,
+ I2C_SMBUS_WRITE, pdata->sel_reg_addr,
+ I2C_SMBUS_BYTE_DATA, &data);
}
static int mlxcpld_mux_select_chan(struct i2c_mux_core *muxc, u32 chan)
diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c
index 6a39adaf433f..9e75d6b9140b 100644
--- a/drivers/i2c/muxes/i2c-mux-pca9541.c
+++ b/drivers/i2c/muxes/i2c-mux-pca9541.c
@@ -99,31 +99,11 @@ MODULE_DEVICE_TABLE(of, pca9541_of_match);
static int pca9541_reg_write(struct i2c_client *client, u8 command, u8 val)
{
struct i2c_adapter *adap = client->adapter;
- int ret;
-
- if (adap->algo->master_xfer) {
- struct i2c_msg msg;
- char buf[2];
-
- msg.addr = client->addr;
- msg.flags = 0;
- msg.len = 2;
- buf[0] = command;
- buf[1] = val;
- msg.buf = buf;
- ret = __i2c_transfer(adap, &msg, 1);
- } else {
- union i2c_smbus_data data;
-
- data.byte = val;
- ret = adap->algo->smbus_xfer(adap, client->addr,
- client->flags,
- I2C_SMBUS_WRITE,
- command,
- I2C_SMBUS_BYTE_DATA, &data);
- }
+ union i2c_smbus_data data = { .byte = val };
- return ret;
+ return __i2c_smbus_xfer(adap, client->addr, client->flags,
+ I2C_SMBUS_WRITE, command,
+ I2C_SMBUS_BYTE_DATA, &data);
}
/*
@@ -133,41 +113,14 @@ static int pca9541_reg_write(struct i2c_client *client, u8 command, u8 val)
static int pca9541_reg_read(struct i2c_client *client, u8 command)
{
struct i2c_adapter *adap = client->adapter;
+ union i2c_smbus_data data;
int ret;
- u8 val;
-
- if (adap->algo->master_xfer) {
- struct i2c_msg msg[2] = {
- {
- .addr = client->addr,
- .flags = 0,
- .len = 1,
- .buf = &command
- },
- {
- .addr = client->addr,
- .flags = I2C_M_RD,
- .len = 1,
- .buf = &val
- }
- };
- ret = __i2c_transfer(adap, msg, 2);
- if (ret == 2)
- ret = val;
- else if (ret >= 0)
- ret = -EIO;
- } else {
- union i2c_smbus_data data;
-
- ret = adap->algo->smbus_xfer(adap, client->addr,
- client->flags,
- I2C_SMBUS_READ,
- command,
- I2C_SMBUS_BYTE_DATA, &data);
- if (!ret)
- ret = data.byte;
- }
- return ret;
+
+ ret = __i2c_smbus_xfer(adap, client->addr, client->flags,
+ I2C_SMBUS_READ, command,
+ I2C_SMBUS_BYTE_DATA, &data);
+
+ return ret ?: data.byte;
}
/*
@@ -345,11 +298,11 @@ static int pca9541_probe(struct i2c_client *client,
/*
* I2C accesses are unprotected here.
- * We have to lock the adapter before releasing the bus.
+ * We have to lock the I2C segment before releasing the bus.
*/
- i2c_lock_adapter(adap);
+ i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
pca9541_release_bus(client);
- i2c_unlock_adapter(adap);
+ i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
/* Create mux adapter */
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index fbc748027087..24bd9275fde5 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -220,30 +220,11 @@ MODULE_DEVICE_TABLE(of, pca954x_of_match);
static int pca954x_reg_write(struct i2c_adapter *adap,
struct i2c_client *client, u8 val)
{
- int ret = -ENODEV;
-
- if (adap->algo->master_xfer) {
- struct i2c_msg msg;
- char buf[1];
-
- msg.addr = client->addr;
- msg.flags = 0;
- msg.len = 1;
- buf[0] = val;
- msg.buf = buf;
- ret = __i2c_transfer(adap, &msg, 1);
-
- if (ret >= 0 && ret != 1)
- ret = -EREMOTEIO;
- } else {
- union i2c_smbus_data data;
- ret = adap->algo->smbus_xfer(adap, client->addr,
- client->flags,
- I2C_SMBUS_WRITE,
- val, I2C_SMBUS_BYTE, &data);
- }
+ union i2c_smbus_data dummy;
- return ret;
+ return __i2c_smbus_xfer(adap, client->addr, client->flags,
+ I2C_SMBUS_WRITE, val,
+ I2C_SMBUS_BYTE, &dummy);
}
static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan)
@@ -368,7 +349,8 @@ static int pca954x_probe(struct i2c_client *client,
{
struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
- struct device_node *of_node = client->dev.of_node;
+ struct device *dev = &client->dev;
+ struct device_node *np = dev->of_node;
bool idle_disconnect_dt;
struct gpio_desc *gpio;
int num, force, class;
@@ -379,8 +361,7 @@ static int pca954x_probe(struct i2c_client *client,
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
return -ENODEV;
- muxc = i2c_mux_alloc(adap, &client->dev,
- PCA954X_MAX_NCHANS, sizeof(*data), 0,
+ muxc = i2c_mux_alloc(adap, dev, PCA954X_MAX_NCHANS, sizeof(*data), 0,
pca954x_select_chan, pca954x_deselect_mux);
if (!muxc)
return -ENOMEM;
@@ -390,7 +371,7 @@ static int pca954x_probe(struct i2c_client *client,
data->client = client;
/* Reset the mux if a reset GPIO is specified. */
- gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH);
+ gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
if (gpio) {
@@ -400,7 +381,7 @@ static int pca954x_probe(struct i2c_client *client,
udelay(1);
}
- data->chip = of_device_get_match_data(&client->dev);
+ data->chip = of_device_get_match_data(dev);
if (!data->chip)
data->chip = &chips[id->driver_data];
@@ -414,8 +395,7 @@ static int pca954x_probe(struct i2c_client *client,
if (!ret &&
(id.manufacturer_id != data->chip->id.manufacturer_id ||
id.part_id != data->chip->id.part_id)) {
- dev_warn(&client->dev,
- "unexpected device id %03x-%03x-%x\n",
+ dev_warn(dev, "unexpected device id %03x-%03x-%x\n",
id.manufacturer_id, id.part_id,
id.die_revision);
return -ENODEV;
@@ -427,14 +407,14 @@ static int pca954x_probe(struct i2c_client *client,
* initializes the mux to disconnected state.
*/
if (i2c_smbus_write_byte(client, 0) < 0) {
- dev_warn(&client->dev, "probe failed\n");
+ dev_warn(dev, "probe failed\n");
return -ENODEV;
}
data->last_chan = 0; /* force the first selection */
- idle_disconnect_dt = of_node &&
- of_property_read_bool(of_node, "i2c-mux-idle-disconnect");
+ idle_disconnect_dt = np &&
+ of_property_read_bool(np, "i2c-mux-idle-disconnect");
ret = pca954x_irq_setup(muxc);
if (ret)
@@ -465,7 +445,7 @@ static int pca954x_probe(struct i2c_client *client,
}
if (data->irq) {
- ret = devm_request_threaded_irq(&client->dev, data->client->irq,
+ ret = devm_request_threaded_irq(dev, data->client->irq,
NULL, pca954x_irq_handler,
IRQF_ONESHOT | IRQF_SHARED,
"pca954x", data);
@@ -473,8 +453,7 @@ static int pca954x_probe(struct i2c_client *client,
goto fail_cleanup;
}
- dev_info(&client->dev,
- "registered %d multiplexed busses for I2C %s %s\n",
+ dev_info(dev, "registered %d multiplexed busses for I2C %s %s\n",
num, data->chip->muxtype == pca954x_ismux
? "mux" : "switch", client->name);
diff --git a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c
index e52c58c29d9a..4d565b0c5a6e 100644
--- a/drivers/ide/hpt366.c
+++ b/drivers/ide/hpt366.c
@@ -574,13 +574,14 @@ static u8 hpt3xx_udma_filter(ide_drive_t *drive)
if (!HPT370_ALLOW_ATA100_5 ||
check_in_drive_list(drive, bad_ata100_5))
return ATA_UDMA4;
+ /* else: fall through */
case HPT372 :
case HPT372A:
case HPT372N:
case HPT374 :
if (ata_id_is_sata(drive->id))
mask &= ~0x0e;
- /* Fall thru */
+ /* fall through */
default:
return mask;
}
@@ -600,7 +601,7 @@ static u8 hpt3xx_mdma_filter(ide_drive_t *drive)
case HPT374 :
if (ata_id_is_sata(drive->id))
return 0x00;
- /* Fall thru */
+ /* else: fall through */
default:
return 0x07;
}
diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c
index 5bd2aafc3753..a8df300f949c 100644
--- a/drivers/ide/ide-floppy.c
+++ b/drivers/ide/ide-floppy.c
@@ -427,6 +427,7 @@ static int ide_floppy_get_capacity(ide_drive_t *drive)
* (maintains previous driver behaviour)
*/
break;
+ /* else: fall through */
case CAPACITY_CURRENT:
/* Normal Zip/LS-120 disks */
if (memcmp(cap_desc, &floppy->cap_desc, 8))
diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c
index a444bad7a2aa..0d93e0cfbeaf 100644
--- a/drivers/ide/ide-io.c
+++ b/drivers/ide/ide-io.c
@@ -460,7 +460,6 @@ void do_ide_request(struct request_queue *q)
struct ide_host *host = hwif->host;
struct request *rq = NULL;
ide_startstop_t startstop;
- unsigned long queue_run_ms = 3; /* old plug delay */
spin_unlock_irq(q->queue_lock);
@@ -480,9 +479,6 @@ repeat:
prev_port = hwif->host->cur_port;
if (drive->dev_flags & IDE_DFLAG_SLEEPING &&
time_after(drive->sleep, jiffies)) {
- unsigned long left = jiffies - drive->sleep;
-
- queue_run_ms = jiffies_to_msecs(left + 1);
ide_unlock_port(hwif);
goto plug_device;
}
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
index 416a2f353071..3b75a7b7a284 100644
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -142,6 +142,7 @@ static void ide_classify_atapi_dev(ide_drive_t *drive)
}
/* Early cdrom models used zero */
type = ide_cdrom;
+ /* fall through */
case ide_cdrom:
drive->dev_flags |= IDE_DFLAG_REMOVABLE;
#ifdef CONFIG_PPC
diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c
index aee7b46d2330..34c1165226a4 100644
--- a/drivers/ide/ide-tape.c
+++ b/drivers/ide/ide-tape.c
@@ -1746,7 +1746,6 @@ static void idetape_setup(ide_drive_t *drive, idetape_tape_t *tape, int minor)
{
unsigned long t;
int speed;
- int buffer_size;
u16 *ctl = (u16 *)&tape->caps[12];
ide_debug_log(IDE_DBG_FUNC, "minor: %d", minor);
@@ -1781,7 +1780,6 @@ static void idetape_setup(ide_drive_t *drive, idetape_tape_t *tape, int minor)
*ctl /= 2;
tape->buffer_size = *ctl * tape->blk_size;
}
- buffer_size = tape->buffer_size;
/* select the "best" DSC read/write polling freq */
speed = max(*(u16 *)&tape->caps[14], *(u16 *)&tape->caps[8]);
diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c
index 89b29028d315..c21d5c50ae3a 100644
--- a/drivers/ide/ide-taskfile.c
+++ b/drivers/ide/ide-taskfile.c
@@ -128,7 +128,7 @@ ide_startstop_t do_rw_taskfile(ide_drive_t *drive, struct ide_cmd *orig_cmd)
return pre_task_out_intr(drive, cmd);
}
handler = task_pio_intr;
- /* fall-through */
+ /* fall through */
case ATA_PROT_NODATA:
if (handler == NULL)
handler = task_no_data_intr;
@@ -140,6 +140,7 @@ ide_startstop_t do_rw_taskfile(ide_drive_t *drive, struct ide_cmd *orig_cmd)
hwif->expiry = dma_ops->dma_timer_expiry;
ide_execute_command(drive, cmd, ide_dma_intr, 2 * WAIT_CMD);
dma_ops->dma_start(drive);
+ /* fall through */
default:
return ide_started;
}
diff --git a/drivers/ide/sis5513.c b/drivers/ide/sis5513.c
index c3062b53056f..024bc7ba49ee 100644
--- a/drivers/ide/sis5513.c
+++ b/drivers/ide/sis5513.c
@@ -494,6 +494,7 @@ static int init_chipset_sis5513(struct pci_dev *dev)
pci_read_config_byte(dev, 0x09, &reg);
if ((reg & 0x0f) != 0x00)
pci_write_config_byte(dev, 0x09, reg&0xf0);
+ /* fall through */
case ATA_16:
/* force per drive recovery and active timings
needed on ATA_33 and below chips */
diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c
index d619e8634a00..13a4cec64ea8 100644
--- a/drivers/iio/temperature/mlx90614.c
+++ b/drivers/iio/temperature/mlx90614.c
@@ -433,11 +433,11 @@ static int mlx90614_wakeup(struct mlx90614_data *data)
dev_dbg(&data->client->dev, "Requesting wake-up");
- i2c_lock_adapter(data->client->adapter);
+ i2c_lock_bus(data->client->adapter, I2C_LOCK_ROOT_ADAPTER);
gpiod_direction_output(data->wakeup_gpio, 0);
msleep(MLX90614_TIMING_WAKEUP);
gpiod_direction_input(data->wakeup_gpio);
- i2c_unlock_adapter(data->client->adapter);
+ i2c_unlock_bus(data->client->adapter, I2C_LOCK_ROOT_ADAPTER);
data->ready_timestamp = jiffies +
msecs_to_jiffies(MLX90614_TIMING_STARTUP);
diff --git a/drivers/input/touchscreen/rohm_bu21023.c b/drivers/input/touchscreen/rohm_bu21023.c
index bda0500c9b57..714affdd742f 100644
--- a/drivers/input/touchscreen/rohm_bu21023.c
+++ b/drivers/input/touchscreen/rohm_bu21023.c
@@ -304,7 +304,7 @@ static int rohm_i2c_burst_read(struct i2c_client *client, u8 start, void *buf,
msg[1].len = len;
msg[1].buf = buf;
- i2c_lock_adapter(adap);
+ i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
for (i = 0; i < 2; i++) {
if (__i2c_transfer(adap, &msg[i], 1) < 0) {
@@ -313,7 +313,7 @@ static int rohm_i2c_burst_read(struct i2c_client *client, u8 start, void *buf,
}
}
- i2c_unlock_adapter(adap);
+ i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
return ret;
}
diff --git a/drivers/irqchip/irq-renesas-h8s.c b/drivers/irqchip/irq-renesas-h8s.c
index aed31afb0216..85234d456638 100644
--- a/drivers/irqchip/irq-renesas-h8s.c
+++ b/drivers/irqchip/irq-renesas-h8s.c
@@ -12,7 +12,7 @@
#include <asm/io.h>
static void *intc_baseaddr;
-#define IPRA ((unsigned long)intc_baseaddr)
+#define IPRA (intc_baseaddr)
static const unsigned char ipr_table[] = {
0x03, 0x02, 0x01, 0x00, 0x13, 0x12, 0x11, 0x10, /* 16 - 23 */
@@ -34,7 +34,7 @@ static const unsigned char ipr_table[] = {
static void h8s_disable_irq(struct irq_data *data)
{
int pos;
- unsigned int addr;
+ void __iomem *addr;
unsigned short pri;
int irq = data->irq;
@@ -48,7 +48,7 @@ static void h8s_disable_irq(struct irq_data *data)
static void h8s_enable_irq(struct irq_data *data)
{
int pos;
- unsigned int addr;
+ void __iomem *addr;
unsigned short pri;
int irq = data->irq;
diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c
index f3acbb57d48c..35a93b251aab 100644
--- a/drivers/media/dvb-frontends/af9013.c
+++ b/drivers/media/dvb-frontends/af9013.c
@@ -1311,10 +1311,10 @@ static int af9013_wregs(struct i2c_client *client, u8 cmd, u16 reg,
memcpy(&buf[3], val, len);
if (lock)
- i2c_lock_adapter(client->adapter);
+ i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
ret = __i2c_transfer(client->adapter, msg, 1);
if (lock)
- i2c_unlock_adapter(client->adapter);
+ i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
if (ret < 0) {
goto err;
} else if (ret != 1) {
@@ -1352,10 +1352,10 @@ static int af9013_rregs(struct i2c_client *client, u8 cmd, u16 reg,
buf[2] = cmd;
if (lock)
- i2c_lock_adapter(client->adapter);
+ i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
ret = __i2c_transfer(client->adapter, msg, 2);
if (lock)
- i2c_unlock_adapter(client->adapter);
+ i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
if (ret < 0) {
goto err;
} else if (ret != 2) {
diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c
index f1886945a7bc..84ac3f73f8fe 100644
--- a/drivers/media/dvb-frontends/drxk_hard.c
+++ b/drivers/media/dvb-frontends/drxk_hard.c
@@ -213,7 +213,7 @@ static inline u32 log10times100(u32 value)
static int drxk_i2c_lock(struct drxk_state *state)
{
- i2c_lock_adapter(state->i2c);
+ i2c_lock_bus(state->i2c, I2C_LOCK_SEGMENT);
state->drxk_i2c_exclusive_lock = true;
return 0;
@@ -224,7 +224,7 @@ static void drxk_i2c_unlock(struct drxk_state *state)
if (!state->drxk_i2c_exclusive_lock)
return;
- i2c_unlock_adapter(state->i2c);
+ i2c_unlock_bus(state->i2c, I2C_LOCK_SEGMENT);
state->drxk_i2c_exclusive_lock = false;
}
diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c
index adc9046d5a90..c0659568471b 100644
--- a/drivers/media/dvb-frontends/rtl2830.c
+++ b/drivers/media/dvb-frontends/rtl2830.c
@@ -24,9 +24,9 @@ static int rtl2830_bulk_write(struct i2c_client *client, unsigned int reg,
struct rtl2830_dev *dev = i2c_get_clientdata(client);
int ret;
- i2c_lock_adapter(client->adapter);
+ i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
ret = regmap_bulk_write(dev->regmap, reg, val, val_count);
- i2c_unlock_adapter(client->adapter);
+ i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
return ret;
}
@@ -36,9 +36,9 @@ static int rtl2830_update_bits(struct i2c_client *client, unsigned int reg,
struct rtl2830_dev *dev = i2c_get_clientdata(client);
int ret;
- i2c_lock_adapter(client->adapter);
+ i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
ret = regmap_update_bits(dev->regmap, reg, mask, val);
- i2c_unlock_adapter(client->adapter);
+ i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
return ret;
}
@@ -48,9 +48,9 @@ static int rtl2830_bulk_read(struct i2c_client *client, unsigned int reg,
struct rtl2830_dev *dev = i2c_get_clientdata(client);
int ret;
- i2c_lock_adapter(client->adapter);
+ i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
ret = regmap_bulk_read(dev->regmap, reg, val, val_count);
- i2c_unlock_adapter(client->adapter);
+ i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
return ret;
}
diff --git a/drivers/media/dvb-frontends/tda1004x.c b/drivers/media/dvb-frontends/tda1004x.c
index d402e4b722ca..e506f66657bb 100644
--- a/drivers/media/dvb-frontends/tda1004x.c
+++ b/drivers/media/dvb-frontends/tda1004x.c
@@ -329,7 +329,7 @@ static int tda1004x_do_upload(struct tda1004x_state *state,
tda1004x_write_byteI(state, dspCodeCounterReg, 0);
fw_msg.addr = state->config->demod_address;
- i2c_lock_adapter(state->i2c);
+ i2c_lock_bus(state->i2c, I2C_LOCK_SEGMENT);
buf[0] = dspCodeInReg;
while (pos != len) {
// work out how much to send this time
@@ -342,14 +342,14 @@ static int tda1004x_do_upload(struct tda1004x_state *state,
fw_msg.len = tx_size + 1;
if (__i2c_transfer(state->i2c, &fw_msg, 1) != 1) {
printk(KERN_ERR "tda1004x: Error during firmware upload\n");
- i2c_unlock_adapter(state->i2c);
+ i2c_unlock_bus(state->i2c, I2C_LOCK_SEGMENT);
return -EIO;
}
pos += tx_size;
dprintk("%s: fw_pos=0x%x\n", __func__, pos);
}
- i2c_unlock_adapter(state->i2c);
+ i2c_unlock_bus(state->i2c, I2C_LOCK_SEGMENT);
/* give the DSP a chance to settle 03/10/05 Hac */
msleep(100);
diff --git a/drivers/media/tuners/tda18271-common.c b/drivers/media/tuners/tda18271-common.c
index 7e81cd887c13..054b3b747dae 100644
--- a/drivers/media/tuners/tda18271-common.c
+++ b/drivers/media/tuners/tda18271-common.c
@@ -225,7 +225,7 @@ static int __tda18271_write_regs(struct dvb_frontend *fe, int idx, int len,
*/
if (lock_i2c) {
tda18271_i2c_gate_ctrl(fe, 1);
- i2c_lock_adapter(priv->i2c_props.adap);
+ i2c_lock_bus(priv->i2c_props.adap, I2C_LOCK_SEGMENT);
}
while (len) {
if (max > len)
@@ -246,7 +246,7 @@ static int __tda18271_write_regs(struct dvb_frontend *fe, int idx, int len,
len -= max;
}
if (lock_i2c) {
- i2c_unlock_adapter(priv->i2c_props.adap);
+ i2c_unlock_bus(priv->i2c_props.adap, I2C_LOCK_SEGMENT);
tda18271_i2c_gate_ctrl(fe, 0);
}
@@ -300,7 +300,7 @@ int tda18271_init_regs(struct dvb_frontend *fe)
* as those could cause bad things
*/
tda18271_i2c_gate_ctrl(fe, 1);
- i2c_lock_adapter(priv->i2c_props.adap);
+ i2c_lock_bus(priv->i2c_props.adap, I2C_LOCK_SEGMENT);
/* initialize registers */
switch (priv->id) {
@@ -516,7 +516,7 @@ int tda18271_init_regs(struct dvb_frontend *fe)
/* synchronize */
__tda18271_write_regs(fe, R_EP1, 1, false);
- i2c_unlock_adapter(priv->i2c_props.adap);
+ i2c_unlock_bus(priv->i2c_props.adap, I2C_LOCK_SEGMENT);
tda18271_i2c_gate_ctrl(fe, 0);
return 0;
diff --git a/drivers/mfd/88pm860x-i2c.c b/drivers/mfd/88pm860x-i2c.c
index 84e313107233..7b9052ea7413 100644
--- a/drivers/mfd/88pm860x-i2c.c
+++ b/drivers/mfd/88pm860x-i2c.c
@@ -146,14 +146,14 @@ int pm860x_page_reg_write(struct i2c_client *i2c, int reg,
unsigned char zero;
int ret;
- i2c_lock_adapter(i2c->adapter);
+ i2c_lock_bus(i2c->adapter, I2C_LOCK_SEGMENT);
read_device(i2c, 0xFA, 0, &zero);
read_device(i2c, 0xFB, 0, &zero);
read_device(i2c, 0xFF, 0, &zero);
ret = write_device(i2c, reg, 1, &data);
read_device(i2c, 0xFE, 0, &zero);
read_device(i2c, 0xFC, 0, &zero);
- i2c_unlock_adapter(i2c->adapter);
+ i2c_unlock_bus(i2c->adapter, I2C_LOCK_SEGMENT);
return ret;
}
EXPORT_SYMBOL(pm860x_page_reg_write);
@@ -164,14 +164,14 @@ int pm860x_page_bulk_read(struct i2c_client *i2c, int reg,
unsigned char zero = 0;
int ret;
- i2c_lock_adapter(i2c->adapter);
+ i2c_lock_bus(i2c->adapter, I2C_LOCK_SEGMENT);
read_device(i2c, 0xfa, 0, &zero);
read_device(i2c, 0xfb, 0, &zero);
read_device(i2c, 0xff, 0, &zero);
ret = read_device(i2c, reg, count, buf);
read_device(i2c, 0xFE, 0, &zero);
read_device(i2c, 0xFC, 0, &zero);
- i2c_unlock_adapter(i2c->adapter);
+ i2c_unlock_bus(i2c->adapter, I2C_LOCK_SEGMENT);
return ret;
}
EXPORT_SYMBOL(pm860x_page_bulk_read);
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index f5cc517d1131..7e50e1d6f58c 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -478,6 +478,23 @@ static void at24_properties_to_pdata(struct device *dev,
if (device_property_present(dev, "no-read-rollover"))
chip->flags |= AT24_FLAG_NO_RDROL;
+ err = device_property_read_u32(dev, "address-width", &val);
+ if (!err) {
+ switch (val) {
+ case 8:
+ if (chip->flags & AT24_FLAG_ADDR16)
+ dev_warn(dev, "Override address width to be 8, while default is 16\n");
+ chip->flags &= ~AT24_FLAG_ADDR16;
+ break;
+ case 16:
+ chip->flags |= AT24_FLAG_ADDR16;
+ break;
+ default:
+ dev_warn(dev, "Bad \"address-width\" property: %u\n",
+ val);
+ }
+ }
+
err = device_property_read_u32(dev, "size", &val);
if (!err)
chip->byte_len = val;
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 2bbefe828670..ebd07ad82431 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -3217,7 +3217,7 @@ static int tun_chr_fasync(int fd, struct file *file, int on)
goto out;
if (on) {
- __f_setown(file, task_pid(current), PIDTYPE_PID, 0);
+ __f_setown(file, task_pid(current), PIDTYPE_TGID, 0);
tfile->flags |= TUN_FASYNC;
} else
tfile->flags &= ~TUN_FASYNC;
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 738e3546abb1..c2ab57705043 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -615,13 +615,11 @@ static bool acpi_pci_need_resume(struct pci_dev *dev)
/*
* In some cases (eg. Samsung 305V4A) leaving a bridge in suspend over
* system-wide suspend/resume confuses the platform firmware, so avoid
- * doing that, unless the bridge has a driver that should take care of
- * the PM handling. According to Section 16.1.6 of ACPI 6.2, endpoint
+ * doing that. According to Section 16.1.6 of ACPI 6.2, endpoint
* devices are expected to be in D3 before invoking the S3 entry path
* from the firmware, so they should not be affected by this issue.
*/
- if (pci_is_bridge(dev) && !dev->driver &&
- acpi_target_system_state() != ACPI_STATE_S0)
+ if (pci_is_bridge(dev) && acpi_target_system_state() != ACPI_STATE_S0)
return true;
if (!adev || !acpi_device_power_manageable(adev))
diff --git a/drivers/platform/chrome/chromeos_tbmc.c b/drivers/platform/chrome/chromeos_tbmc.c
index b935df6a9694..1e81f8144c0d 100644
--- a/drivers/platform/chrome/chromeos_tbmc.c
+++ b/drivers/platform/chrome/chromeos_tbmc.c
@@ -1,8 +1,16 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0
// Driver to detect Tablet Mode for ChromeOS convertible.
//
// Copyright (C) 2017 Google, Inc.
// Author: Gwendal Grignou <gwendal@chromium.org>
+//
+// On Chromebook using ACPI, this device listens for notification
+// from GOOG0006 and issue method TBMC to retrieve the status.
+//
+// GOOG0006 issues the notification when it receives EC_HOST_EVENT_MODE_CHANGE
+// from the EC.
+// Method TBMC reads EC_ACPI_MEM_DEVICE_ORIENTATION byte from the shared
+// memory region.
#include <linux/acpi.h>
#include <linux/input.h>
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index cae9b0595692..d556e95c532c 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -57,6 +57,7 @@
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/delay.h>
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index df58fc878b3e..6533aa560aa1 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -104,6 +104,17 @@ config POWER_RESET_MSM
help
Power off and restart support for Qualcomm boards.
+config POWER_RESET_QCOM_PON
+ tristate "Qualcomm power-on driver"
+ depends on ARCH_QCOM
+ depends on MFD_SPMI_PMIC
+ select REBOOT_MODE
+ help
+ Power On support for Qualcomm boards.
+ If you have a Qualcomm platform and need support for
+ power-on and reboot reason, Say Y.
+ If unsure, Say N.
+
config POWER_RESET_OCELOT_RESET
bool "Microsemi Ocelot reset driver"
depends on MSCC_OCELOT || COMPILE_TEST
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
index 7778c7485cf1..0aebee954ac1 100644
--- a/drivers/power/reset/Makefile
+++ b/drivers/power/reset/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o
obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o
obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
+obj-$(CONFIG_POWER_RESET_QCOM_PON) += qcom-pon.o
obj-$(CONFIG_POWER_RESET_OCELOT_RESET) += ocelot-reset.o
obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o
obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o
diff --git a/drivers/power/reset/gemini-poweroff.c b/drivers/power/reset/gemini-poweroff.c
index 2ac291af1265..90e35c07240a 100644
--- a/drivers/power/reset/gemini-poweroff.c
+++ b/drivers/power/reset/gemini-poweroff.c
@@ -130,7 +130,17 @@ static int gemini_poweroff_probe(struct platform_device *pdev)
val |= GEMINI_CTRL_ENABLE;
writel(val, gpw->base + GEMINI_PWC_CTRLREG);
- /* Now that the state machine is active, clear the IRQ */
+ /* Clear the IRQ */
+ val = readl(gpw->base + GEMINI_PWC_CTRLREG);
+ val |= GEMINI_CTRL_IRQ_CLR;
+ writel(val, gpw->base + GEMINI_PWC_CTRLREG);
+
+ /* Wait for this to clear */
+ val = readl(gpw->base + GEMINI_PWC_STATREG);
+ while (val & 0x70U)
+ val = readl(gpw->base + GEMINI_PWC_STATREG);
+
+ /* Clear the IRQ again */
val = readl(gpw->base + GEMINI_PWC_CTRLREG);
val |= GEMINI_CTRL_IRQ_CLR;
writel(val, gpw->base + GEMINI_PWC_CTRLREG);
diff --git a/drivers/power/reset/qcom-pon.c b/drivers/power/reset/qcom-pon.c
new file mode 100644
index 000000000000..0c4caaa7e88f
--- /dev/null
+++ b/drivers/power/reset/qcom-pon.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017-18 Linaro Limited
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/reboot-mode.h>
+#include <linux/regmap.h>
+
+#define PON_SOFT_RB_SPARE 0x8f
+
+struct pm8916_pon {
+ struct device *dev;
+ struct regmap *regmap;
+ u32 baseaddr;
+ struct reboot_mode_driver reboot_mode;
+};
+
+static int pm8916_reboot_mode_write(struct reboot_mode_driver *reboot,
+ unsigned int magic)
+{
+ struct pm8916_pon *pon = container_of
+ (reboot, struct pm8916_pon, reboot_mode);
+ int ret;
+
+ ret = regmap_update_bits(pon->regmap,
+ pon->baseaddr + PON_SOFT_RB_SPARE,
+ 0xfc, magic << 2);
+ if (ret < 0)
+ dev_err(pon->dev, "update reboot mode bits failed\n");
+
+ return ret;
+}
+
+static int pm8916_pon_probe(struct platform_device *pdev)
+{
+ struct pm8916_pon *pon;
+ int error;
+
+ pon = devm_kzalloc(&pdev->dev, sizeof(*pon), GFP_KERNEL);
+ if (!pon)
+ return -ENOMEM;
+
+ pon->dev = &pdev->dev;
+
+ pon->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!pon->regmap) {
+ dev_err(&pdev->dev, "failed to locate regmap\n");
+ return -ENODEV;
+ }
+
+ error = of_property_read_u32(pdev->dev.of_node, "reg",
+ &pon->baseaddr);
+ if (error)
+ return error;
+
+ pon->reboot_mode.dev = &pdev->dev;
+ pon->reboot_mode.write = pm8916_reboot_mode_write;
+ error = devm_reboot_mode_register(&pdev->dev, &pon->reboot_mode);
+ if (error) {
+ dev_err(&pdev->dev, "can't register reboot mode\n");
+ return error;
+ }
+
+ platform_set_drvdata(pdev, pon);
+
+ return devm_of_platform_populate(&pdev->dev);
+}
+
+static const struct of_device_id pm8916_pon_id_table[] = {
+ { .compatible = "qcom,pm8916-pon" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pm8916_pon_id_table);
+
+static struct platform_driver pm8916_pon_driver = {
+ .probe = pm8916_pon_probe,
+ .driver = {
+ .name = "pm8916-pon",
+ .of_match_table = of_match_ptr(pm8916_pon_id_table),
+ },
+};
+module_platform_driver(pm8916_pon_driver);
+
+MODULE_DESCRIPTION("pm8916 Power On driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/reset/vexpress-poweroff.c b/drivers/power/reset/vexpress-poweroff.c
index 102f95a09460..e9e749f87517 100644
--- a/drivers/power/reset/vexpress-poweroff.c
+++ b/drivers/power/reset/vexpress-poweroff.c
@@ -35,6 +35,7 @@ static void vexpress_reset_do(struct device *dev, const char *what)
}
static struct device *vexpress_power_off_device;
+static atomic_t vexpress_restart_nb_refcnt = ATOMIC_INIT(0);
static void vexpress_power_off(void)
{
@@ -99,10 +100,13 @@ static int _vexpress_register_restart_handler(struct device *dev)
int err;
vexpress_restart_device = dev;
- err = register_restart_handler(&vexpress_restart_nb);
- if (err) {
- dev_err(dev, "cannot register restart handler (err=%d)\n", err);
- return err;
+ if (atomic_inc_return(&vexpress_restart_nb_refcnt) == 1) {
+ err = register_restart_handler(&vexpress_restart_nb);
+ if (err) {
+ dev_err(dev, "cannot register restart handler (err=%d)\n", err);
+ atomic_dec(&vexpress_restart_nb_refcnt);
+ return err;
+ }
}
device_create_file(dev, &dev_attr_active);
diff --git a/drivers/power/reset/zx-reboot.c b/drivers/power/reset/zx-reboot.c
index c03e96e6a041..186901c96c01 100644
--- a/drivers/power/reset/zx-reboot.c
+++ b/drivers/power/reset/zx-reboot.c
@@ -51,6 +51,7 @@ static int zx_reboot_probe(struct platform_device *pdev)
np = of_find_compatible_node(NULL, NULL, "zte,zx296702-pcu");
pcu_base = of_iomap(np, 0);
+ of_node_put(np);
if (!pcu_base) {
iounmap(base);
WARN(1, "failed to map pcu_base address");
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 428b426842f4..ff6dab0bf0dd 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -75,6 +75,17 @@ config BATTERY_88PM860X
help
Say Y here to enable battery monitor for Marvell 88PM860x chip.
+config CHARGER_ADP5061
+ tristate "ADP5061 battery charger driver"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say Y here to enable support for the ADP5061 standalone battery
+ charger.
+
+ This driver can be built as a module. If so, the module will be
+ called adp5061.
+
config BATTERY_ACT8945A
tristate "Active-semi ACT8945A charger driver"
depends on MFD_ACT8945A || COMPILE_TEST
@@ -92,7 +103,7 @@ config BATTERY_CPCAP
config BATTERY_DS2760
tristate "DS2760 battery driver (HP iPAQ & others)"
- depends on W1 && W1_SLAVE_DS2760
+ depends on W1
help
Say Y here to enable support for batteries with ds2760 chip.
@@ -624,4 +635,14 @@ config CHARGER_RT9455
help
Say Y to enable support for Richtek RT9455 battery charger.
+config CHARGER_CROS_USBPD
+ tristate "ChromeOS EC based USBPD charger"
+ depends on MFD_CROS_EC
+ default n
+ help
+ Say Y here to enable ChromeOS EC based USBPD charger
+ driver. This driver gets various bits of information about
+ what is connected to USB PD ports from the EC and converts
+ that into power_supply properties.
+
endif # POWER_SUPPLY
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index e83aa843bcc6..a26b402c45d9 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_WM8350_POWER) += wm8350_power.o
obj-$(CONFIG_TEST_POWER) += test_power.o
obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o
+obj-$(CONFIG_CHARGER_ADP5061) += adp5061.o
obj-$(CONFIG_BATTERY_ACT8945A) += act8945a_charger.o
obj-$(CONFIG_BATTERY_AXP20X) += axp20x_battery.o
obj-$(CONFIG_CHARGER_AXP20X) += axp20x_ac_power.o
@@ -83,3 +84,4 @@ obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o
obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o
obj-$(CONFIG_AXP288_CHARGER) += axp288_charger.o
+obj-$(CONFIG_CHARGER_CROS_USBPD) += cros_usbpd-charger.o
diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c
index d9c6c7bedd85..02356f9b5f22 100644
--- a/drivers/power/supply/ab8500_fg.c
+++ b/drivers/power/supply/ab8500_fg.c
@@ -379,15 +379,13 @@ static int ab8500_fg_is_low_curr(struct ab8500_fg *di, int curr)
*/
static int ab8500_fg_add_cap_sample(struct ab8500_fg *di, int sample)
{
- struct timespec64 ts64;
+ time64_t now = ktime_get_boottime_seconds();
struct ab8500_fg_avg_cap *avg = &di->avg_cap;
- getnstimeofday64(&ts64);
-
do {
avg->sum += sample - avg->samples[avg->pos];
avg->samples[avg->pos] = sample;
- avg->time_stamps[avg->pos] = ts64.tv_sec;
+ avg->time_stamps[avg->pos] = now;
avg->pos++;
if (avg->pos == NBR_AVG_SAMPLES)
@@ -400,7 +398,7 @@ static int ab8500_fg_add_cap_sample(struct ab8500_fg *di, int sample)
* Check the time stamp for each sample. If too old,
* replace with latest sample
*/
- } while (ts64.tv_sec - VALID_CAPACITY_SEC > avg->time_stamps[avg->pos]);
+ } while (now - VALID_CAPACITY_SEC > avg->time_stamps[avg->pos]);
avg->avg = avg->sum / avg->nbr_samples;
@@ -439,14 +437,14 @@ static void ab8500_fg_clear_cap_samples(struct ab8500_fg *di)
static void ab8500_fg_fill_cap_sample(struct ab8500_fg *di, int sample)
{
int i;
- struct timespec64 ts64;
+ time64_t now;
struct ab8500_fg_avg_cap *avg = &di->avg_cap;
- getnstimeofday64(&ts64);
+ now = ktime_get_boottime_seconds();
for (i = 0; i < NBR_AVG_SAMPLES; i++) {
avg->samples[i] = sample;
- avg->time_stamps[i] = ts64.tv_sec;
+ avg->time_stamps[i] = now;
}
avg->pos = 0;
diff --git a/drivers/power/supply/adp5061.c b/drivers/power/supply/adp5061.c
new file mode 100644
index 000000000000..939fd3d8fb1a
--- /dev/null
+++ b/drivers/power/supply/adp5061.c
@@ -0,0 +1,745 @@
+/*
+ * ADP5061 I2C Programmable Linear Battery Charger
+ *
+ * Copyright 2018 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/mod_devicetable.h>
+#include <linux/power_supply.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+/* ADP5061 registers definition */
+#define ADP5061_ID 0x00
+#define ADP5061_REV 0x01
+#define ADP5061_VINX_SET 0x02
+#define ADP5061_TERM_SET 0x03
+#define ADP5061_CHG_CURR 0x04
+#define ADP5061_VOLTAGE_TH 0x05
+#define ADP5061_TIMER_SET 0x06
+#define ADP5061_FUNC_SET_1 0x07
+#define ADP5061_FUNC_SET_2 0x08
+#define ADP5061_INT_EN 0x09
+#define ADP5061_INT_ACT 0x0A
+#define ADP5061_CHG_STATUS_1 0x0B
+#define ADP5061_CHG_STATUS_2 0x0C
+#define ADP5061_FAULT 0x0D
+#define ADP5061_BATTERY_SHORT 0x10
+#define ADP5061_IEND 0x11
+
+/* ADP5061_VINX_SET */
+#define ADP5061_VINX_SET_ILIM_MSK GENMASK(3, 0)
+#define ADP5061_VINX_SET_ILIM_MODE(x) (((x) & 0x0F) << 0)
+
+/* ADP5061_TERM_SET */
+#define ADP5061_TERM_SET_VTRM_MSK GENMASK(7, 2)
+#define ADP5061_TERM_SET_VTRM_MODE(x) (((x) & 0x3F) << 2)
+#define ADP5061_TERM_SET_CHG_VLIM_MSK GENMASK(1, 0)
+#define ADP5061_TERM_SET_CHG_VLIM_MODE(x) (((x) & 0x03) << 0)
+
+/* ADP5061_CHG_CURR */
+#define ADP5061_CHG_CURR_ICHG_MSK GENMASK(6, 2)
+#define ADP5061_CHG_CURR_ICHG_MODE(x) (((x) & 0x1F) << 2)
+#define ADP5061_CHG_CURR_ITRK_DEAD_MSK GENMASK(1, 0)
+#define ADP5061_CHG_CURR_ITRK_DEAD_MODE(x) (((x) & 0x03) << 0)
+
+/* ADP5061_VOLTAGE_TH */
+#define ADP5061_VOLTAGE_TH_DIS_RCH_MSK BIT(7)
+#define ADP5061_VOLTAGE_TH_DIS_RCH_MODE(x) (((x) & 0x01) << 7)
+#define ADP5061_VOLTAGE_TH_VRCH_MSK GENMASK(6, 5)
+#define ADP5061_VOLTAGE_TH_VRCH_MODE(x) (((x) & 0x03) << 5)
+#define ADP5061_VOLTAGE_TH_VTRK_DEAD_MSK GENMASK(4, 3)
+#define ADP5061_VOLTAGE_TH_VTRK_DEAD_MODE(x) (((x) & 0x03) << 3)
+#define ADP5061_VOLTAGE_TH_VWEAK_MSK GENMASK(2, 0)
+#define ADP5061_VOLTAGE_TH_VWEAK_MODE(x) (((x) & 0x07) << 0)
+
+/* ADP5061_CHG_STATUS_1 */
+#define ADP5061_CHG_STATUS_1_VIN_OV(x) (((x) >> 7) & 0x1)
+#define ADP5061_CHG_STATUS_1_VIN_OK(x) (((x) >> 6) & 0x1)
+#define ADP5061_CHG_STATUS_1_VIN_ILIM(x) (((x) >> 5) & 0x1)
+#define ADP5061_CHG_STATUS_1_THERM_LIM(x) (((x) >> 4) & 0x1)
+#define ADP5061_CHG_STATUS_1_CHDONE(x) (((x) >> 3) & 0x1)
+#define ADP5061_CHG_STATUS_1_CHG_STATUS(x) (((x) >> 0) & 0x7)
+
+/* ADP5061_CHG_STATUS_2 */
+#define ADP5061_CHG_STATUS_2_THR_STATUS(x) (((x) >> 5) & 0x7)
+#define ADP5061_CHG_STATUS_2_RCH_LIM_INFO(x) (((x) >> 3) & 0x1)
+#define ADP5061_CHG_STATUS_2_BAT_STATUS(x) (((x) >> 0) & 0x7)
+
+/* ADP5061_IEND */
+#define ADP5061_IEND_IEND_MSK GENMASK(7, 5)
+#define ADP5061_IEND_IEND_MODE(x) (((x) & 0x07) << 5)
+
+#define ADP5061_NO_BATTERY 0x01
+#define ADP5061_ICHG_MAX 1300 // mA
+
+enum adp5061_chg_status {
+ ADP5061_CHG_OFF,
+ ADP5061_CHG_TRICKLE,
+ ADP5061_CHG_FAST_CC,
+ ADP5061_CHG_FAST_CV,
+ ADP5061_CHG_COMPLETE,
+ ADP5061_CHG_LDO_MODE,
+ ADP5061_CHG_TIMER_EXP,
+ ADP5061_CHG_BAT_DET,
+};
+
+static const int adp5061_chg_type[4] = {
+ [ADP5061_CHG_OFF] = POWER_SUPPLY_CHARGE_TYPE_NONE,
+ [ADP5061_CHG_TRICKLE] = POWER_SUPPLY_CHARGE_TYPE_TRICKLE,
+ [ADP5061_CHG_FAST_CC] = POWER_SUPPLY_CHARGE_TYPE_FAST,
+ [ADP5061_CHG_FAST_CV] = POWER_SUPPLY_CHARGE_TYPE_FAST,
+};
+
+static const int adp5061_vweak_th[8] = {
+ 2700, 2800, 2900, 3000, 3100, 3200, 3300, 3400,
+};
+
+static const int adp5061_prechg_current[4] = {
+ 5, 10, 20, 80,
+};
+
+static const int adp5061_vmin[4] = {
+ 2000, 2500, 2600, 2900,
+};
+
+static const int adp5061_const_chg_vmax[4] = {
+ 3200, 3400, 3700, 3800,
+};
+
+static const int adp5061_const_ichg[24] = {
+ 50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650,
+ 700, 750, 800, 850, 900, 950, 1000, 1050, 1100, 1200, 1300,
+};
+
+static const int adp5061_vmax[36] = {
+ 3800, 3820, 3840, 3860, 3880, 3900, 3920, 3940, 3960, 3980,
+ 4000, 4020, 4040, 4060, 4080, 4100, 4120, 4140, 4160, 4180,
+ 4200, 4220, 4240, 4260, 4280, 4300, 4320, 4340, 4360, 4380,
+ 4400, 4420, 4440, 4460, 4480, 4500,
+};
+
+static const int adp5061_in_current_lim[16] = {
+ 100, 150, 200, 250, 300, 400, 500, 600, 700,
+ 800, 900, 1000, 1200, 1500, 1800, 2100,
+};
+
+static const int adp5061_iend[8] = {
+ 12500, 32500, 52500, 72500, 92500, 117500, 142500, 170000,
+};
+
+struct adp5061_state {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct power_supply *psy;
+};
+
+static int adp5061_get_array_index(const int *array, u8 size, int val)
+{
+ int i;
+
+ for (i = 1; i < size; i++) {
+ if (val < array[i])
+ break;
+ }
+
+ return i-1;
+}
+
+static int adp5061_get_status(struct adp5061_state *st,
+ u8 *status1, u8 *status2)
+{
+ u8 buf[2];
+ int ret;
+
+ /* CHG_STATUS1 and CHG_STATUS2 are adjacent regs */
+ ret = regmap_bulk_read(st->regmap, ADP5061_CHG_STATUS_1,
+ &buf[0], 2);
+ if (ret < 0)
+ return ret;
+
+ *status1 = buf[0];
+ *status2 = buf[1];
+
+ return ret;
+}
+
+static int adp5061_get_input_current_limit(struct adp5061_state *st,
+ union power_supply_propval *val)
+{
+ unsigned int regval;
+ int mode, ret;
+
+ ret = regmap_read(st->regmap, ADP5061_VINX_SET, &regval);
+ if (ret < 0)
+ return ret;
+
+ mode = ADP5061_VINX_SET_ILIM_MODE(regval);
+ val->intval = adp5061_in_current_lim[mode] * 1000;
+
+ return ret;
+}
+
+static int adp5061_set_input_current_limit(struct adp5061_state *st, int val)
+{
+ int index;
+
+ /* Convert from uA to mA */
+ val /= 1000;
+ index = adp5061_get_array_index(adp5061_in_current_lim,
+ ARRAY_SIZE(adp5061_in_current_lim),
+ val);
+ if (index < 0)
+ return index;
+
+ return regmap_update_bits(st->regmap, ADP5061_VINX_SET,
+ ADP5061_VINX_SET_ILIM_MSK,
+ ADP5061_VINX_SET_ILIM_MODE(index));
+}
+
+static int adp5061_set_min_voltage(struct adp5061_state *st, int val)
+{
+ int index;
+
+ /* Convert from uV to mV */
+ val /= 1000;
+ index = adp5061_get_array_index(adp5061_vmin,
+ ARRAY_SIZE(adp5061_vmin),
+ val);
+ if (index < 0)
+ return index;
+
+ return regmap_update_bits(st->regmap, ADP5061_VOLTAGE_TH,
+ ADP5061_VOLTAGE_TH_VTRK_DEAD_MSK,
+ ADP5061_VOLTAGE_TH_VTRK_DEAD_MODE(index));
+}
+
+static int adp5061_get_min_voltage(struct adp5061_state *st,
+ union power_supply_propval *val)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(st->regmap, ADP5061_VOLTAGE_TH, &regval);
+ if (ret < 0)
+ return ret;
+
+ regval = ((regval & ADP5061_VOLTAGE_TH_VTRK_DEAD_MSK) >> 3);
+ val->intval = adp5061_vmin[regval] * 1000;
+
+ return ret;
+}
+
+static int adp5061_get_chg_volt_lim(struct adp5061_state *st,
+ union power_supply_propval *val)
+{
+ unsigned int regval;
+ int mode, ret;
+
+ ret = regmap_read(st->regmap, ADP5061_TERM_SET, &regval);
+ if (ret < 0)
+ return ret;
+
+ mode = ADP5061_TERM_SET_CHG_VLIM_MODE(regval);
+ val->intval = adp5061_const_chg_vmax[mode] * 1000;
+
+ return ret;
+}
+
+static int adp5061_get_max_voltage(struct adp5061_state *st,
+ union power_supply_propval *val)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(st->regmap, ADP5061_TERM_SET, &regval);
+ if (ret < 0)
+ return ret;
+
+ regval = ((regval & ADP5061_TERM_SET_VTRM_MSK) >> 2) - 0x0F;
+ if (regval >= ARRAY_SIZE(adp5061_vmax))
+ regval = ARRAY_SIZE(adp5061_vmax) - 1;
+
+ val->intval = adp5061_vmax[regval] * 1000;
+
+ return ret;
+}
+
+static int adp5061_set_max_voltage(struct adp5061_state *st, int val)
+{
+ int vmax_index;
+
+ /* Convert from uV to mV */
+ val /= 1000;
+ if (val > 4500)
+ val = 4500;
+
+ vmax_index = adp5061_get_array_index(adp5061_vmax,
+ ARRAY_SIZE(adp5061_vmax), val);
+ if (vmax_index < 0)
+ return vmax_index;
+
+ vmax_index += 0x0F;
+
+ return regmap_update_bits(st->regmap, ADP5061_TERM_SET,
+ ADP5061_TERM_SET_VTRM_MSK,
+ ADP5061_TERM_SET_VTRM_MODE(vmax_index));
+}
+
+static int adp5061_set_const_chg_vmax(struct adp5061_state *st, int val)
+{
+ int index;
+
+ /* Convert from uV to mV */
+ val /= 1000;
+ index = adp5061_get_array_index(adp5061_const_chg_vmax,
+ ARRAY_SIZE(adp5061_const_chg_vmax),
+ val);
+ if (index < 0)
+ return index;
+
+ return regmap_update_bits(st->regmap, ADP5061_TERM_SET,
+ ADP5061_TERM_SET_CHG_VLIM_MSK,
+ ADP5061_TERM_SET_CHG_VLIM_MODE(index));
+}
+
+static int adp5061_set_const_chg_current(struct adp5061_state *st, int val)
+{
+
+ int index;
+
+ /* Convert from uA to mA */
+ val /= 1000;
+ if (val > ADP5061_ICHG_MAX)
+ val = ADP5061_ICHG_MAX;
+
+ index = adp5061_get_array_index(adp5061_const_ichg,
+ ARRAY_SIZE(adp5061_const_ichg),
+ val);
+ if (index < 0)
+ return index;
+
+ return regmap_update_bits(st->regmap, ADP5061_CHG_CURR,
+ ADP5061_CHG_CURR_ICHG_MSK,
+ ADP5061_CHG_CURR_ICHG_MODE(index));
+}
+
+static int adp5061_get_const_chg_current(struct adp5061_state *st,
+ union power_supply_propval *val)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(st->regmap, ADP5061_CHG_CURR, &regval);
+ if (ret < 0)
+ return ret;
+
+ regval = ((regval & ADP5061_CHG_CURR_ICHG_MSK) >> 2);
+ if (regval >= ARRAY_SIZE(adp5061_const_ichg))
+ regval = ARRAY_SIZE(adp5061_const_ichg) - 1;
+
+ val->intval = adp5061_const_ichg[regval] * 1000;
+
+ return ret;
+}
+
+static int adp5061_get_prechg_current(struct adp5061_state *st,
+ union power_supply_propval *val)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(st->regmap, ADP5061_CHG_CURR, &regval);
+ if (ret < 0)
+ return ret;
+
+ regval &= ADP5061_CHG_CURR_ITRK_DEAD_MSK;
+ val->intval = adp5061_prechg_current[regval] * 1000;
+
+ return ret;
+}
+
+static int adp5061_set_prechg_current(struct adp5061_state *st, int val)
+{
+ int index;
+
+ /* Convert from uA to mA */
+ val /= 1000;
+ index = adp5061_get_array_index(adp5061_prechg_current,
+ ARRAY_SIZE(adp5061_prechg_current),
+ val);
+ if (index < 0)
+ return index;
+
+ return regmap_update_bits(st->regmap, ADP5061_CHG_CURR,
+ ADP5061_CHG_CURR_ITRK_DEAD_MSK,
+ ADP5061_CHG_CURR_ITRK_DEAD_MODE(index));
+}
+
+static int adp5061_get_vweak_th(struct adp5061_state *st,
+ union power_supply_propval *val)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(st->regmap, ADP5061_VOLTAGE_TH, &regval);
+ if (ret < 0)
+ return ret;
+
+ regval &= ADP5061_VOLTAGE_TH_VWEAK_MSK;
+ val->intval = adp5061_vweak_th[regval] * 1000;
+
+ return ret;
+}
+
+static int adp5061_set_vweak_th(struct adp5061_state *st, int val)
+{
+ int index;
+
+ /* Convert from uV to mV */
+ val /= 1000;
+ index = adp5061_get_array_index(adp5061_vweak_th,
+ ARRAY_SIZE(adp5061_vweak_th),
+ val);
+ if (index < 0)
+ return index;
+
+ return regmap_update_bits(st->regmap, ADP5061_VOLTAGE_TH,
+ ADP5061_VOLTAGE_TH_VWEAK_MSK,
+ ADP5061_VOLTAGE_TH_VWEAK_MODE(index));
+}
+
+static int adp5061_get_chg_type(struct adp5061_state *st,
+ union power_supply_propval *val)
+{
+ u8 status1, status2;
+ int chg_type, ret;
+
+ ret = adp5061_get_status(st, &status1, &status2);
+ if (ret < 0)
+ return ret;
+
+ chg_type = adp5061_chg_type[ADP5061_CHG_STATUS_1_CHG_STATUS(status1)];
+ if (chg_type > ADP5061_CHG_FAST_CV)
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ else
+ val->intval = chg_type;
+
+ return ret;
+}
+
+static int adp5061_get_charger_status(struct adp5061_state *st,
+ union power_supply_propval *val)
+{
+ u8 status1, status2;
+ int ret;
+
+ ret = adp5061_get_status(st, &status1, &status2);
+ if (ret < 0)
+ return ret;
+
+ switch (ADP5061_CHG_STATUS_1_CHG_STATUS(status1)) {
+ case ADP5061_CHG_OFF:
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+ case ADP5061_CHG_TRICKLE:
+ case ADP5061_CHG_FAST_CC:
+ case ADP5061_CHG_FAST_CV:
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ case ADP5061_CHG_COMPLETE:
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ break;
+ case ADP5061_CHG_TIMER_EXP:
+ /* The battery must be discharging if there is a charge fault */
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ break;
+ default:
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ }
+
+ return ret;
+}
+
+static int adp5061_get_battery_status(struct adp5061_state *st,
+ union power_supply_propval *val)
+{
+ u8 status1, status2;
+ int ret;
+
+ ret = adp5061_get_status(st, &status1, &status2);
+ if (ret < 0)
+ return ret;
+
+ switch (ADP5061_CHG_STATUS_2_BAT_STATUS(status2)) {
+ case 0x0: /* Battery monitor off */
+ case 0x1: /* No battery */
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
+ break;
+ case 0x2: /* VBAT < VTRK */
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+ break;
+ case 0x3: /* VTRK < VBAT_SNS < VWEAK */
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+ break;
+ case 0x4: /* VBAT_SNS > VWEAK */
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int adp5061_get_termination_current(struct adp5061_state *st,
+ union power_supply_propval *val)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(st->regmap, ADP5061_IEND, &regval);
+ if (ret < 0)
+ return ret;
+
+ regval = (regval & ADP5061_IEND_IEND_MSK) >> 5;
+ val->intval = adp5061_iend[regval];
+
+ return ret;
+}
+
+static int adp5061_set_termination_current(struct adp5061_state *st, int val)
+{
+ int index;
+
+ index = adp5061_get_array_index(adp5061_iend,
+ ARRAY_SIZE(adp5061_iend),
+ val);
+ if (index < 0)
+ return index;
+
+ return regmap_update_bits(st->regmap, ADP5061_IEND,
+ ADP5061_IEND_IEND_MSK,
+ ADP5061_IEND_IEND_MODE(index));
+}
+
+static int adp5061_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct adp5061_state *st = power_supply_get_drvdata(psy);
+ u8 status1, status2;
+ int mode, ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ ret = adp5061_get_status(st, &status1, &status2);
+ if (ret < 0)
+ return ret;
+
+ mode = ADP5061_CHG_STATUS_2_BAT_STATUS(status2);
+ if (mode == ADP5061_NO_BATTERY)
+ val->intval = 0;
+ else
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ return adp5061_get_chg_type(st, val);
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ /* This property is used to indicate the input current
+ * limit into VINx (ILIM)
+ */
+ return adp5061_get_input_current_limit(st, val);
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ /* This property is used to indicate the termination
+ * voltage (VTRM)
+ */
+ return adp5061_get_max_voltage(st, val);
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+ /*
+ * This property is used to indicate the trickle to fast
+ * charge threshold (VTRK_DEAD)
+ */
+ return adp5061_get_min_voltage(st, val);
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+ /* This property is used to indicate the charging
+ * voltage limit (CHG_VLIM)
+ */
+ return adp5061_get_chg_volt_lim(st, val);
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ /*
+ * This property is used to indicate the value of the constant
+ * current charge (ICHG)
+ */
+ return adp5061_get_const_chg_current(st, val);
+ case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
+ /*
+ * This property is used to indicate the value of the trickle
+ * and weak charge currents (ITRK_DEAD)
+ */
+ return adp5061_get_prechg_current(st, val);
+ case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+ /*
+ * This property is used to set the VWEAK threshold
+ * bellow this value, weak charge mode is entered
+ * above this value, fast chargerge mode is entered
+ */
+ return adp5061_get_vweak_th(st, val);
+ case POWER_SUPPLY_PROP_STATUS:
+ /*
+ * Indicate the charger status in relation to power
+ * supply status property
+ */
+ return adp5061_get_charger_status(st, val);
+ case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+ /*
+ * Indicate the battery status in relation to power
+ * supply capacity level property
+ */
+ return adp5061_get_battery_status(st, val);
+ case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+ /* Indicate the values of the termination current */
+ return adp5061_get_termination_current(st, val);
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int adp5061_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct adp5061_state *st = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ return adp5061_set_input_current_limit(st, val->intval);
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ return adp5061_set_max_voltage(st, val->intval);
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+ return adp5061_set_min_voltage(st, val->intval);
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+ return adp5061_set_const_chg_vmax(st, val->intval);
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ return adp5061_set_const_chg_current(st, val->intval);
+ case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
+ return adp5061_set_prechg_current(st, val->intval);
+ case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+ return adp5061_set_vweak_th(st, val->intval);
+ case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+ return adp5061_set_termination_current(st, val->intval);
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int adp5061_prop_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
+ case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+ case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static enum power_supply_property adp5061_props[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+ POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
+ POWER_SUPPLY_PROP_VOLTAGE_AVG,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
+};
+
+static const struct regmap_config adp5061_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static const struct power_supply_desc adp5061_desc = {
+ .name = "adp5061",
+ .type = POWER_SUPPLY_TYPE_USB,
+ .get_property = adp5061_get_property,
+ .set_property = adp5061_set_property,
+ .property_is_writeable = adp5061_prop_writeable,
+ .properties = adp5061_props,
+ .num_properties = ARRAY_SIZE(adp5061_props),
+};
+
+static int adp5061_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct power_supply_config psy_cfg = {};
+ struct adp5061_state *st;
+
+ st = devm_kzalloc(&client->dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ st->client = client;
+ st->regmap = devm_regmap_init_i2c(client,
+ &adp5061_regmap_config);
+ if (IS_ERR(st->regmap)) {
+ dev_err(&client->dev, "Failed to initialize register map\n");
+ return -EINVAL;
+ }
+
+ i2c_set_clientdata(client, st);
+ psy_cfg.drv_data = st;
+
+ st->psy = devm_power_supply_register(&client->dev,
+ &adp5061_desc,
+ &psy_cfg);
+
+ if (IS_ERR(st->psy)) {
+ dev_err(&client->dev, "Failed to register power supply\n");
+ return PTR_ERR(st->psy);
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id adp5061_id[] = {
+ { "adp5061", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adp5061_id);
+
+static struct i2c_driver adp5061_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ },
+ .probe = adp5061_probe,
+ .id_table = adp5061_id,
+};
+module_i2c_driver(adp5061_driver);
+
+MODULE_DESCRIPTION("Analog Devices adp5061 battery charger driver");
+MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
index 44f70dcea61e..42001df4bd13 100644
--- a/drivers/power/supply/axp20x_usb_power.c
+++ b/drivers/power/supply/axp20x_usb_power.c
@@ -222,6 +222,7 @@ static int axp20x_usb_power_set_current_max(struct axp20x_usb_power *power,
case 100000:
if (power->axp20x_id == AXP221_ID)
return -EINVAL;
+ /* fall through */
case 500000:
case 900000:
val = (900000 - intval) / 400000;
diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c
index 6e1bc14c3304..735658ee1c60 100644
--- a/drivers/power/supply/axp288_charger.c
+++ b/drivers/power/supply/axp288_charger.c
@@ -718,7 +718,7 @@ static int charger_init_hw_regs(struct axp288_chrg_info *info)
}
/* Determine charge current limit */
- cc = (ret & CHRG_CCCV_CC_MASK) >> CHRG_CCCV_CC_BIT_POS;
+ cc = (val & CHRG_CCCV_CC_MASK) >> CHRG_CCCV_CC_BIT_POS;
cc = (cc * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
info->cc = cc;
diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
index d44ed8e17c47..f022e1b550df 100644
--- a/drivers/power/supply/bq27xxx_battery.c
+++ b/drivers/power/supply/bq27xxx_battery.c
@@ -26,7 +26,6 @@
* http://www.ti.com/product/bq27510-g1
* http://www.ti.com/product/bq27510-g2
* http://www.ti.com/product/bq27510-g3
- * http://www.ti.com/product/bq27520-g4
* http://www.ti.com/product/bq27520-g1
* http://www.ti.com/product/bq27520-g2
* http://www.ti.com/product/bq27520-g3
@@ -40,7 +39,9 @@
* http://www.ti.com/product/bq27545-g1
* http://www.ti.com/product/bq27421-g1
* http://www.ti.com/product/bq27425-g1
+ * http://www.ti.com/product/bq27426
* http://www.ti.com/product/bq27411-g1
+ * http://www.ti.com/product/bq27441-g1
* http://www.ti.com/product/bq27621-g1
*/
diff --git a/drivers/power/supply/cros_usbpd-charger.c b/drivers/power/supply/cros_usbpd-charger.c
new file mode 100644
index 000000000000..688a16bacfbb
--- /dev/null
+++ b/drivers/power/supply/cros_usbpd-charger.c
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Power supply driver for ChromeOS EC based USB PD Charger.
+ *
+ * Copyright (c) 2014 - 2018 Google, Inc
+ */
+
+#include <linux/module.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+
+#define CHARGER_DIR_NAME "CROS_USBPD_CHARGER%d"
+#define CHARGER_DIR_NAME_LENGTH sizeof(CHARGER_DIR_NAME)
+#define CHARGER_CACHE_UPDATE_DELAY msecs_to_jiffies(500)
+#define CHARGER_MANUFACTURER_MODEL_LENGTH 32
+
+#define DRV_NAME "cros-usbpd-charger"
+
+struct port_data {
+ int port_number;
+ char name[CHARGER_DIR_NAME_LENGTH];
+ char manufacturer[CHARGER_MANUFACTURER_MODEL_LENGTH];
+ char model_name[CHARGER_MANUFACTURER_MODEL_LENGTH];
+ struct power_supply *psy;
+ struct power_supply_desc psy_desc;
+ int psy_usb_type;
+ int psy_online;
+ int psy_status;
+ int psy_current_max;
+ int psy_voltage_max_design;
+ int psy_voltage_now;
+ int psy_power_max;
+ struct charger_data *charger;
+ unsigned long last_update;
+};
+
+struct charger_data {
+ struct device *dev;
+ struct cros_ec_dev *ec_dev;
+ struct cros_ec_device *ec_device;
+ int num_charger_ports;
+ int num_registered_psy;
+ struct port_data *ports[EC_USB_PD_MAX_PORTS];
+ struct notifier_block notifier;
+};
+
+static enum power_supply_property cros_usbpd_charger_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+ POWER_SUPPLY_PROP_USB_TYPE
+};
+
+static enum power_supply_usb_type cros_usbpd_charger_usb_types[] = {
+ POWER_SUPPLY_USB_TYPE_UNKNOWN,
+ POWER_SUPPLY_USB_TYPE_SDP,
+ POWER_SUPPLY_USB_TYPE_DCP,
+ POWER_SUPPLY_USB_TYPE_CDP,
+ POWER_SUPPLY_USB_TYPE_C,
+ POWER_SUPPLY_USB_TYPE_PD,
+ POWER_SUPPLY_USB_TYPE_PD_DRP,
+ POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID
+};
+
+static int cros_usbpd_charger_ec_command(struct charger_data *charger,
+ unsigned int version,
+ unsigned int command,
+ void *outdata,
+ unsigned int outsize,
+ void *indata,
+ unsigned int insize)
+{
+ struct cros_ec_dev *ec_dev = charger->ec_dev;
+ struct cros_ec_command *msg;
+ int ret;
+
+ msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->version = version;
+ msg->command = ec_dev->cmd_offset + command;
+ msg->outsize = outsize;
+ msg->insize = insize;
+
+ if (outsize)
+ memcpy(msg->data, outdata, outsize);
+
+ ret = cros_ec_cmd_xfer_status(charger->ec_device, msg);
+ if (ret >= 0 && insize)
+ memcpy(indata, msg->data, insize);
+
+ kfree(msg);
+ return ret;
+}
+
+static int cros_usbpd_charger_get_num_ports(struct charger_data *charger)
+{
+ struct ec_response_usb_pd_ports resp;
+ int ret;
+
+ ret = cros_usbpd_charger_ec_command(charger, 0, EC_CMD_USB_PD_PORTS,
+ NULL, 0, &resp, sizeof(resp));
+ if (ret < 0) {
+ dev_err(charger->dev,
+ "Unable to get the number or ports (err:0x%x)\n", ret);
+ return ret;
+ }
+
+ return resp.num_ports;
+}
+
+static int cros_usbpd_charger_get_discovery_info(struct port_data *port)
+{
+ struct charger_data *charger = port->charger;
+ struct ec_params_usb_pd_discovery_entry resp;
+ struct ec_params_usb_pd_info_request req;
+ int ret;
+
+ req.port = port->port_number;
+
+ ret = cros_usbpd_charger_ec_command(charger, 0,
+ EC_CMD_USB_PD_DISCOVERY,
+ &req, sizeof(req),
+ &resp, sizeof(resp));
+ if (ret < 0) {
+ dev_err(charger->dev,
+ "Unable to query discovery info (err:0x%x)\n", ret);
+ return ret;
+ }
+
+ dev_dbg(charger->dev, "Port %d: VID = 0x%x, PID=0x%x, PTYPE=0x%x\n",
+ port->port_number, resp.vid, resp.pid, resp.ptype);
+
+ snprintf(port->manufacturer, sizeof(port->manufacturer), "%x",
+ resp.vid);
+ snprintf(port->model_name, sizeof(port->model_name), "%x", resp.pid);
+
+ return 0;
+}
+
+static int cros_usbpd_charger_get_power_info(struct port_data *port)
+{
+ struct charger_data *charger = port->charger;
+ struct ec_response_usb_pd_power_info resp;
+ struct ec_params_usb_pd_power_info req;
+ int last_psy_status, last_psy_usb_type;
+ struct device *dev = charger->dev;
+ int ret;
+
+ req.port = port->port_number;
+ ret = cros_usbpd_charger_ec_command(charger, 0,
+ EC_CMD_USB_PD_POWER_INFO,
+ &req, sizeof(req),
+ &resp, sizeof(resp));
+ if (ret < 0) {
+ dev_err(dev, "Unable to query PD power info (err:0x%x)\n", ret);
+ return ret;
+ }
+
+ last_psy_status = port->psy_status;
+ last_psy_usb_type = port->psy_usb_type;
+
+ switch (resp.role) {
+ case USB_PD_PORT_POWER_DISCONNECTED:
+ port->psy_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ port->psy_online = 0;
+ break;
+ case USB_PD_PORT_POWER_SOURCE:
+ port->psy_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ port->psy_online = 0;
+ break;
+ case USB_PD_PORT_POWER_SINK:
+ port->psy_status = POWER_SUPPLY_STATUS_CHARGING;
+ port->psy_online = 1;
+ break;
+ case USB_PD_PORT_POWER_SINK_NOT_CHARGING:
+ port->psy_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ port->psy_online = 1;
+ break;
+ default:
+ dev_err(dev, "Unknown role %d\n", resp.role);
+ break;
+ }
+
+ port->psy_voltage_max_design = resp.meas.voltage_max;
+ port->psy_voltage_now = resp.meas.voltage_now;
+ port->psy_current_max = resp.meas.current_max;
+ port->psy_power_max = resp.max_power;
+
+ switch (resp.type) {
+ case USB_CHG_TYPE_BC12_SDP:
+ case USB_CHG_TYPE_VBUS:
+ port->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP;
+ break;
+ case USB_CHG_TYPE_NONE:
+ /*
+ * For dual-role devices when we are a source, the firmware
+ * reports the type as NONE. Report such chargers as type
+ * USB_PD_DRP.
+ */
+ if (resp.role == USB_PD_PORT_POWER_SOURCE && resp.dualrole)
+ port->psy_usb_type = POWER_SUPPLY_USB_TYPE_PD_DRP;
+ else
+ port->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP;
+ break;
+ case USB_CHG_TYPE_OTHER:
+ case USB_CHG_TYPE_PROPRIETARY:
+ port->psy_usb_type = POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID;
+ break;
+ case USB_CHG_TYPE_C:
+ port->psy_usb_type = POWER_SUPPLY_USB_TYPE_C;
+ break;
+ case USB_CHG_TYPE_BC12_DCP:
+ port->psy_usb_type = POWER_SUPPLY_USB_TYPE_DCP;
+ break;
+ case USB_CHG_TYPE_BC12_CDP:
+ port->psy_usb_type = POWER_SUPPLY_USB_TYPE_CDP;
+ break;
+ case USB_CHG_TYPE_PD:
+ if (resp.dualrole)
+ port->psy_usb_type = POWER_SUPPLY_USB_TYPE_PD_DRP;
+ else
+ port->psy_usb_type = POWER_SUPPLY_USB_TYPE_PD;
+ break;
+ case USB_CHG_TYPE_UNKNOWN:
+ /*
+ * While the EC is trying to determine the type of charger that
+ * has been plugged in, it will report the charger type as
+ * unknown. Additionally since the power capabilities are
+ * unknown, report the max current and voltage as zero.
+ */
+ port->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
+ port->psy_voltage_max_design = 0;
+ port->psy_current_max = 0;
+ break;
+ default:
+ dev_err(dev, "Port %d: default case!\n", port->port_number);
+ port->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP;
+ }
+
+ port->psy_desc.type = POWER_SUPPLY_TYPE_USB;
+
+ dev_dbg(dev,
+ "Port %d: type=%d vmax=%d vnow=%d cmax=%d clim=%d pmax=%d\n",
+ port->port_number, resp.type, resp.meas.voltage_max,
+ resp.meas.voltage_now, resp.meas.current_max,
+ resp.meas.current_lim, resp.max_power);
+
+ /*
+ * If power supply type or status changed, explicitly call
+ * power_supply_changed. This results in udev event getting generated
+ * and allows user mode apps to react quicker instead of waiting for
+ * their next poll of power supply status.
+ */
+ if (last_psy_usb_type != port->psy_usb_type ||
+ last_psy_status != port->psy_status)
+ power_supply_changed(port->psy);
+
+ return 0;
+}
+
+static int cros_usbpd_charger_get_port_status(struct port_data *port,
+ bool ratelimit)
+{
+ int ret;
+
+ if (ratelimit &&
+ time_is_after_jiffies(port->last_update +
+ CHARGER_CACHE_UPDATE_DELAY))
+ return 0;
+
+ ret = cros_usbpd_charger_get_power_info(port);
+ if (ret < 0)
+ return ret;
+
+ ret = cros_usbpd_charger_get_discovery_info(port);
+ port->last_update = jiffies;
+
+ return ret;
+}
+
+static void cros_usbpd_charger_power_changed(struct power_supply *psy)
+{
+ struct port_data *port = power_supply_get_drvdata(psy);
+ struct charger_data *charger = port->charger;
+ int i;
+
+ for (i = 0; i < charger->num_registered_psy; i++)
+ cros_usbpd_charger_get_port_status(charger->ports[i], false);
+}
+
+static int cros_usbpd_charger_get_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct port_data *port = power_supply_get_drvdata(psy);
+ struct charger_data *charger = port->charger;
+ struct cros_ec_device *ec_device = charger->ec_device;
+ struct device *dev = charger->dev;
+ int ret;
+
+ /* Only refresh ec_port_status for dynamic properties */
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ /*
+ * If mkbp_event_supported, then we can be assured that
+ * the driver's state for the online property is consistent
+ * with the hardware. However, if we aren't event driven,
+ * the optimization before to skip an ec_port_status get
+ * and only returned cached values of the online property will
+ * cause a delay in detecting a cable attach until one of the
+ * other properties are read.
+ *
+ * Allow an ec_port_status refresh for online property check
+ * if we're not already online to check for plug events if
+ * not mkbp_event_supported.
+ */
+ if (ec_device->mkbp_event_supported || port->psy_online)
+ break;
+ /* fall through */
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ ret = cros_usbpd_charger_get_port_status(port, true);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get port status (err:0x%x)\n",
+ ret);
+ return -EINVAL;
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = port->psy_online;
+ break;
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = port->psy_status;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ val->intval = port->psy_current_max * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = port->psy_voltage_max_design * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = port->psy_voltage_now * 1000;
+ break;
+ case POWER_SUPPLY_PROP_USB_TYPE:
+ val->intval = port->psy_usb_type;
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = port->model_name;
+ break;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = port->manufacturer;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cros_usbpd_charger_ec_event(struct notifier_block *nb,
+ unsigned long queued_during_suspend,
+ void *_notify)
+{
+ struct cros_ec_device *ec_device;
+ struct charger_data *charger;
+ struct device *dev;
+ u32 host_event;
+
+ charger = container_of(nb, struct charger_data, notifier);
+ ec_device = charger->ec_device;
+ dev = charger->dev;
+
+ host_event = cros_ec_get_host_event(ec_device);
+ if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU)) {
+ cros_usbpd_charger_power_changed(charger->ports[0]->psy);
+ return NOTIFY_OK;
+ } else {
+ return NOTIFY_DONE;
+ }
+}
+
+static void cros_usbpd_charger_unregister_notifier(void *data)
+{
+ struct charger_data *charger = data;
+ struct cros_ec_device *ec_device = charger->ec_device;
+
+ blocking_notifier_chain_unregister(&ec_device->event_notifier,
+ &charger->notifier);
+}
+
+static int cros_usbpd_charger_probe(struct platform_device *pd)
+{
+ struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
+ struct cros_ec_device *ec_device = ec_dev->ec_dev;
+ struct power_supply_desc *psy_desc;
+ struct device *dev = &pd->dev;
+ struct charger_data *charger;
+ struct power_supply *psy;
+ struct port_data *port;
+ int ret = -EINVAL;
+ int i;
+
+ charger = devm_kzalloc(dev, sizeof(struct charger_data),
+ GFP_KERNEL);
+ if (!charger)
+ return -ENOMEM;
+
+ charger->dev = dev;
+ charger->ec_dev = ec_dev;
+ charger->ec_device = ec_device;
+
+ platform_set_drvdata(pd, charger);
+
+ charger->num_charger_ports = cros_usbpd_charger_get_num_ports(charger);
+ if (charger->num_charger_ports <= 0) {
+ /*
+ * This can happen on a system that doesn't support USB PD.
+ * Log a message, but no need to warn.
+ */
+ dev_info(dev, "No charging ports found\n");
+ ret = -ENODEV;
+ goto fail_nowarn;
+ }
+
+ for (i = 0; i < charger->num_charger_ports; i++) {
+ struct power_supply_config psy_cfg = {};
+
+ port = devm_kzalloc(dev, sizeof(struct port_data), GFP_KERNEL);
+ if (!port) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ port->charger = charger;
+ port->port_number = i;
+ sprintf(port->name, CHARGER_DIR_NAME, i);
+
+ psy_desc = &port->psy_desc;
+ psy_desc->name = port->name;
+ psy_desc->type = POWER_SUPPLY_TYPE_USB;
+ psy_desc->get_property = cros_usbpd_charger_get_prop;
+ psy_desc->external_power_changed =
+ cros_usbpd_charger_power_changed;
+ psy_desc->properties = cros_usbpd_charger_props;
+ psy_desc->num_properties =
+ ARRAY_SIZE(cros_usbpd_charger_props);
+ psy_desc->usb_types = cros_usbpd_charger_usb_types;
+ psy_desc->num_usb_types =
+ ARRAY_SIZE(cros_usbpd_charger_usb_types);
+ psy_cfg.drv_data = port;
+
+ psy = devm_power_supply_register_no_ws(dev, psy_desc,
+ &psy_cfg);
+ if (IS_ERR(psy)) {
+ dev_err(dev, "Failed to register power supply\n");
+ continue;
+ }
+ port->psy = psy;
+
+ charger->ports[charger->num_registered_psy++] = port;
+ }
+
+ if (!charger->num_registered_psy) {
+ ret = -ENODEV;
+ dev_err(dev, "No power supplies registered\n");
+ goto fail;
+ }
+
+ if (ec_device->mkbp_event_supported) {
+ /* Get PD events from the EC */
+ charger->notifier.notifier_call = cros_usbpd_charger_ec_event;
+ ret = blocking_notifier_chain_register(
+ &ec_device->event_notifier,
+ &charger->notifier);
+ if (ret < 0) {
+ dev_warn(dev, "failed to register notifier\n");
+ } else {
+ ret = devm_add_action_or_reset(dev,
+ cros_usbpd_charger_unregister_notifier,
+ charger);
+ if (ret < 0)
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ WARN(1, "%s: Failing probe (err:0x%x)\n", dev_name(dev), ret);
+
+fail_nowarn:
+ dev_info(dev, "Failing probe (err:0x%x)\n", ret);
+ return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int cros_usbpd_charger_resume(struct device *dev)
+{
+ struct charger_data *charger = dev_get_drvdata(dev);
+ int i;
+
+ if (!charger)
+ return 0;
+
+ for (i = 0; i < charger->num_registered_psy; i++) {
+ power_supply_changed(charger->ports[i]->psy);
+ charger->ports[i]->last_update =
+ jiffies - CHARGER_CACHE_UPDATE_DELAY;
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(cros_usbpd_charger_pm_ops, NULL,
+ cros_usbpd_charger_resume);
+
+static struct platform_driver cros_usbpd_charger_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .pm = &cros_usbpd_charger_pm_ops,
+ },
+ .probe = cros_usbpd_charger_probe
+};
+
+module_platform_driver(cros_usbpd_charger_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ChromeOS EC USBPD charger");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/power/supply/ds2760_battery.c b/drivers/power/supply/ds2760_battery.c
index ae180dc929c9..11bed88a89fa 100644
--- a/drivers/power/supply/ds2760_battery.c
+++ b/drivers/power/supply/ds2760_battery.c
@@ -27,9 +27,64 @@
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
-
+#include <linux/suspend.h>
#include <linux/w1.h>
-#include "../../w1/slaves/w1_ds2760.h"
+#include <linux/of.h>
+
+static unsigned int cache_time = 1000;
+module_param(cache_time, uint, 0644);
+MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
+
+static bool pmod_enabled;
+module_param(pmod_enabled, bool, 0644);
+MODULE_PARM_DESC(pmod_enabled, "PMOD enable bit");
+
+static unsigned int rated_capacity;
+module_param(rated_capacity, uint, 0644);
+MODULE_PARM_DESC(rated_capacity, "rated battery capacity, 10*mAh or index");
+
+static unsigned int current_accum;
+module_param(current_accum, uint, 0644);
+MODULE_PARM_DESC(current_accum, "current accumulator value");
+
+#define W1_FAMILY_DS2760 0x30
+
+/* Known commands to the DS2760 chip */
+#define W1_DS2760_SWAP 0xAA
+#define W1_DS2760_READ_DATA 0x69
+#define W1_DS2760_WRITE_DATA 0x6C
+#define W1_DS2760_COPY_DATA 0x48
+#define W1_DS2760_RECALL_DATA 0xB8
+#define W1_DS2760_LOCK 0x6A
+
+/* Number of valid register addresses */
+#define DS2760_DATA_SIZE 0x40
+
+#define DS2760_PROTECTION_REG 0x00
+
+#define DS2760_STATUS_REG 0x01
+#define DS2760_STATUS_IE (1 << 2)
+#define DS2760_STATUS_SWEN (1 << 3)
+#define DS2760_STATUS_RNAOP (1 << 4)
+#define DS2760_STATUS_PMOD (1 << 5)
+
+#define DS2760_EEPROM_REG 0x07
+#define DS2760_SPECIAL_FEATURE_REG 0x08
+#define DS2760_VOLTAGE_MSB 0x0c
+#define DS2760_VOLTAGE_LSB 0x0d
+#define DS2760_CURRENT_MSB 0x0e
+#define DS2760_CURRENT_LSB 0x0f
+#define DS2760_CURRENT_ACCUM_MSB 0x10
+#define DS2760_CURRENT_ACCUM_LSB 0x11
+#define DS2760_TEMP_MSB 0x18
+#define DS2760_TEMP_LSB 0x19
+#define DS2760_EEPROM_BLOCK0 0x20
+#define DS2760_ACTIVE_FULL 0x20
+#define DS2760_EEPROM_BLOCK1 0x30
+#define DS2760_STATUS_WRITE_REG 0x31
+#define DS2760_RATED_CAPACITY 0x32
+#define DS2760_CURRENT_OFFSET_BIAS 0x33
+#define DS2760_ACTIVE_EMPTY 0x3b
struct ds2760_device_info {
struct device *dev;
@@ -55,28 +110,113 @@ struct ds2760_device_info {
int full_counter;
struct power_supply *bat;
struct power_supply_desc bat_desc;
- struct device *w1_dev;
struct workqueue_struct *monitor_wqueue;
struct delayed_work monitor_work;
struct delayed_work set_charged_work;
+ struct notifier_block pm_notifier;
};
-static unsigned int cache_time = 1000;
-module_param(cache_time, uint, 0644);
-MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
+static int w1_ds2760_io(struct device *dev, char *buf, int addr, size_t count,
+ int io)
+{
+ struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
-static bool pmod_enabled;
-module_param(pmod_enabled, bool, 0644);
-MODULE_PARM_DESC(pmod_enabled, "PMOD enable bit");
+ if (!dev)
+ return 0;
-static unsigned int rated_capacity;
-module_param(rated_capacity, uint, 0644);
-MODULE_PARM_DESC(rated_capacity, "rated battery capacity, 10*mAh or index");
+ mutex_lock(&sl->master->bus_mutex);
-static unsigned int current_accum;
-module_param(current_accum, uint, 0644);
-MODULE_PARM_DESC(current_accum, "current accumulator value");
+ if (addr > DS2760_DATA_SIZE || addr < 0) {
+ count = 0;
+ goto out;
+ }
+ if (addr + count > DS2760_DATA_SIZE)
+ count = DS2760_DATA_SIZE - addr;
+
+ if (!w1_reset_select_slave(sl)) {
+ if (!io) {
+ w1_write_8(sl->master, W1_DS2760_READ_DATA);
+ w1_write_8(sl->master, addr);
+ count = w1_read_block(sl->master, buf, count);
+ } else {
+ w1_write_8(sl->master, W1_DS2760_WRITE_DATA);
+ w1_write_8(sl->master, addr);
+ w1_write_block(sl->master, buf, count);
+ /* XXX w1_write_block returns void, not n_written */
+ }
+ }
+
+out:
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return count;
+}
+
+static int w1_ds2760_read(struct device *dev,
+ char *buf, int addr,
+ size_t count)
+{
+ return w1_ds2760_io(dev, buf, addr, count, 0);
+}
+
+static int w1_ds2760_write(struct device *dev,
+ char *buf,
+ int addr, size_t count)
+{
+ return w1_ds2760_io(dev, buf, addr, count, 1);
+}
+
+static int w1_ds2760_eeprom_cmd(struct device *dev, int addr, int cmd)
+{
+ struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+
+ if (!dev)
+ return -EINVAL;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ if (w1_reset_select_slave(sl) == 0) {
+ w1_write_8(sl->master, cmd);
+ w1_write_8(sl->master, addr);
+ }
+
+ mutex_unlock(&sl->master->bus_mutex);
+ return 0;
+}
+
+static int w1_ds2760_store_eeprom(struct device *dev, int addr)
+{
+ return w1_ds2760_eeprom_cmd(dev, addr, W1_DS2760_COPY_DATA);
+}
+
+static int w1_ds2760_recall_eeprom(struct device *dev, int addr)
+{
+ return w1_ds2760_eeprom_cmd(dev, addr, W1_DS2760_RECALL_DATA);
+}
+
+static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ return w1_ds2760_read(dev, buf, off, count);
+}
+
+static BIN_ATTR_RO(w1_slave, DS2760_DATA_SIZE);
+
+static struct bin_attribute *w1_ds2760_bin_attrs[] = {
+ &bin_attr_w1_slave,
+ NULL,
+};
+
+static const struct attribute_group w1_ds2760_group = {
+ .bin_attrs = w1_ds2760_bin_attrs,
+};
+static const struct attribute_group *w1_ds2760_groups[] = {
+ &w1_ds2760_group,
+ NULL,
+};
/* Some batteries have their rated capacity stored a N * 10 mAh, while
* others use an index into this table. */
static int rated_capacities[] = {
@@ -138,10 +278,10 @@ static int ds2760_battery_read_status(struct ds2760_device_info *di)
count = DS2760_TEMP_LSB - start + 1;
}
- ret = w1_ds2760_read(di->w1_dev, di->raw + start, start, count);
+ ret = w1_ds2760_read(di->dev, di->raw + start, start, count);
if (ret != count) {
dev_warn(di->dev, "call to w1_ds2760_read failed (0x%p)\n",
- di->w1_dev);
+ di->dev);
return 1;
}
@@ -242,7 +382,7 @@ static void ds2760_battery_set_current_accum(struct ds2760_device_info *di,
acr[0] = acr_val >> 8;
acr[1] = acr_val & 0xff;
- if (w1_ds2760_write(di->w1_dev, acr, DS2760_CURRENT_ACCUM_MSB, 2) < 2)
+ if (w1_ds2760_write(di->dev, acr, DS2760_CURRENT_ACCUM_MSB, 2) < 2)
dev_warn(di->dev, "ACR write failed\n");
}
@@ -297,9 +437,9 @@ static void ds2760_battery_write_status(struct ds2760_device_info *di,
if (status == di->raw[DS2760_STATUS_REG])
return;
- w1_ds2760_write(di->w1_dev, &status, DS2760_STATUS_WRITE_REG, 1);
- w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
- w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
+ w1_ds2760_write(di->dev, &status, DS2760_STATUS_WRITE_REG, 1);
+ w1_ds2760_store_eeprom(di->dev, DS2760_EEPROM_BLOCK1);
+ w1_ds2760_recall_eeprom(di->dev, DS2760_EEPROM_BLOCK1);
}
static void ds2760_battery_write_rated_capacity(struct ds2760_device_info *di,
@@ -308,9 +448,9 @@ static void ds2760_battery_write_rated_capacity(struct ds2760_device_info *di,
if (rated_capacity == di->raw[DS2760_RATED_CAPACITY])
return;
- w1_ds2760_write(di->w1_dev, &rated_capacity, DS2760_RATED_CAPACITY, 1);
- w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
- w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
+ w1_ds2760_write(di->dev, &rated_capacity, DS2760_RATED_CAPACITY, 1);
+ w1_ds2760_store_eeprom(di->dev, DS2760_EEPROM_BLOCK1);
+ w1_ds2760_recall_eeprom(di->dev, DS2760_EEPROM_BLOCK1);
}
static void ds2760_battery_write_active_full(struct ds2760_device_info *di,
@@ -325,9 +465,9 @@ static void ds2760_battery_write_active_full(struct ds2760_device_info *di,
tmp[1] == di->raw[DS2760_ACTIVE_FULL + 1])
return;
- w1_ds2760_write(di->w1_dev, tmp, DS2760_ACTIVE_FULL, sizeof(tmp));
- w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK0);
- w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK0);
+ w1_ds2760_write(di->dev, tmp, DS2760_ACTIVE_FULL, sizeof(tmp));
+ w1_ds2760_store_eeprom(di->dev, DS2760_EEPROM_BLOCK0);
+ w1_ds2760_recall_eeprom(di->dev, DS2760_EEPROM_BLOCK0);
/* Write to the di->raw[] buffer directly - the DS2760_ACTIVE_FULL
* values won't be read back by ds2760_battery_read_status() */
@@ -383,9 +523,9 @@ static void ds2760_battery_set_charged_work(struct work_struct *work)
dev_dbg(di->dev, "%s: bias = %d\n", __func__, bias);
- w1_ds2760_write(di->w1_dev, &bias, DS2760_CURRENT_OFFSET_BIAS, 1);
- w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
- w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
+ w1_ds2760_write(di->dev, &bias, DS2760_CURRENT_OFFSET_BIAS, 1);
+ w1_ds2760_store_eeprom(di->dev, DS2760_EEPROM_BLOCK1);
+ w1_ds2760_recall_eeprom(di->dev, DS2760_EEPROM_BLOCK1);
/* Write to the di->raw[] buffer directly - the CURRENT_OFFSET_BIAS
* value won't be read back by ds2760_battery_read_status() */
@@ -504,24 +644,55 @@ static enum power_supply_property ds2760_battery_props[] = {
POWER_SUPPLY_PROP_CAPACITY,
};
-static int ds2760_battery_probe(struct platform_device *pdev)
+static int ds2760_pm_notifier(struct notifier_block *notifier,
+ unsigned long pm_event,
+ void *unused)
+{
+ struct ds2760_device_info *di =
+ container_of(notifier, struct ds2760_device_info, pm_notifier);
+
+ switch (pm_event) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
+ break;
+
+ case PM_POST_RESTORE:
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
+ power_supply_changed(di->bat);
+ mod_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ);
+
+ break;
+
+ case PM_RESTORE_PREPARE:
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int w1_ds2760_add_slave(struct w1_slave *sl)
{
struct power_supply_config psy_cfg = {};
- char status;
- int retval = 0;
struct ds2760_device_info *di;
+ struct device *dev = &sl->dev;
+ int retval = 0;
+ char name[32];
+ char status;
- di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
+ di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL);
if (!di) {
retval = -ENOMEM;
goto di_alloc_failed;
}
- platform_set_drvdata(pdev, di);
+ snprintf(name, sizeof(name), "ds2760-battery.%d", dev->id);
- di->dev = &pdev->dev;
- di->w1_dev = pdev->dev.parent;
- di->bat_desc.name = dev_name(&pdev->dev);
+ di->dev = dev;
+ di->bat_desc.name = name;
di->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY;
di->bat_desc.properties = ds2760_battery_props;
di->bat_desc.num_properties = ARRAY_SIZE(ds2760_battery_props);
@@ -533,10 +704,30 @@ static int ds2760_battery_probe(struct platform_device *pdev)
di->bat_desc.external_power_changed =
ds2760_battery_external_power_changed;
- psy_cfg.drv_data = di;
+ psy_cfg.drv_data = di;
+
+ if (dev->of_node) {
+ u32 tmp;
+
+ psy_cfg.of_node = dev->of_node;
+
+ if (!of_property_read_bool(dev->of_node, "maxim,pmod-enabled"))
+ pmod_enabled = true;
+
+ if (!of_property_read_u32(dev->of_node,
+ "maxim,cache-time-ms", &tmp))
+ cache_time = tmp;
+
+ if (!of_property_read_u32(dev->of_node,
+ "rated-capacity-microamp-hours",
+ &tmp))
+ rated_capacity = tmp / 10; /* property is in mAh */
+ }
di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
+ sl->family_data = di;
+
/* enable sleep mode feature */
ds2760_battery_read_status(di);
status = di->raw[DS2760_STATUS_REG];
@@ -547,7 +738,7 @@ static int ds2760_battery_probe(struct platform_device *pdev)
ds2760_battery_write_status(di, status);
- /* set rated capacity from module param */
+ /* set rated capacity from module param or device tree */
if (rated_capacity)
ds2760_battery_write_rated_capacity(di, rated_capacity);
@@ -556,7 +747,7 @@ static int ds2760_battery_probe(struct platform_device *pdev)
if (current_accum)
ds2760_battery_set_current_accum(di, current_accum);
- di->bat = power_supply_register(&pdev->dev, &di->bat_desc, &psy_cfg);
+ di->bat = power_supply_register(dev, &di->bat_desc, &psy_cfg);
if (IS_ERR(di->bat)) {
dev_err(di->dev, "failed to register battery\n");
retval = PTR_ERR(di->bat);
@@ -566,14 +757,16 @@ static int ds2760_battery_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&di->monitor_work, ds2760_battery_work);
INIT_DELAYED_WORK(&di->set_charged_work,
ds2760_battery_set_charged_work);
- di->monitor_wqueue = alloc_ordered_workqueue(dev_name(&pdev->dev),
- WQ_MEM_RECLAIM);
+ di->monitor_wqueue = alloc_ordered_workqueue(name, WQ_MEM_RECLAIM);
if (!di->monitor_wqueue) {
retval = -ESRCH;
goto workqueue_failed;
}
queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ * 1);
+ di->pm_notifier.notifier_call = ds2760_pm_notifier;
+ register_pm_notifier(&di->pm_notifier);
+
goto success;
workqueue_failed:
@@ -584,65 +777,40 @@ success:
return retval;
}
-static int ds2760_battery_remove(struct platform_device *pdev)
+static void w1_ds2760_remove_slave(struct w1_slave *sl)
{
- struct ds2760_device_info *di = platform_get_drvdata(pdev);
+ struct ds2760_device_info *di = sl->family_data;
+ unregister_pm_notifier(&di->pm_notifier);
cancel_delayed_work_sync(&di->monitor_work);
cancel_delayed_work_sync(&di->set_charged_work);
destroy_workqueue(di->monitor_wqueue);
power_supply_unregister(di->bat);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-
-static int ds2760_battery_suspend(struct platform_device *pdev,
- pm_message_t state)
-{
- struct ds2760_device_info *di = platform_get_drvdata(pdev);
-
- di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
-
- return 0;
-}
-
-static int ds2760_battery_resume(struct platform_device *pdev)
-{
- struct ds2760_device_info *di = platform_get_drvdata(pdev);
-
- di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
- power_supply_changed(di->bat);
-
- mod_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ);
-
- return 0;
}
-#else
-
-#define ds2760_battery_suspend NULL
-#define ds2760_battery_resume NULL
-
-#endif /* CONFIG_PM */
-
-MODULE_ALIAS("platform:ds2760-battery");
+#ifdef CONFIG_OF
+static const struct of_device_id w1_ds2760_of_ids[] = {
+ { .compatible = "maxim,ds2760" },
+ {}
+};
+#endif
-static struct platform_driver ds2760_battery_driver = {
- .driver = {
- .name = "ds2760-battery",
- },
- .probe = ds2760_battery_probe,
- .remove = ds2760_battery_remove,
- .suspend = ds2760_battery_suspend,
- .resume = ds2760_battery_resume,
+static struct w1_family_ops w1_ds2760_fops = {
+ .add_slave = w1_ds2760_add_slave,
+ .remove_slave = w1_ds2760_remove_slave,
+ .groups = w1_ds2760_groups,
};
-module_platform_driver(ds2760_battery_driver);
+static struct w1_family w1_ds2760_family = {
+ .fid = W1_FAMILY_DS2760,
+ .fops = &w1_ds2760_fops,
+ .of_match_table = of_match_ptr(w1_ds2760_of_ids),
+};
+module_w1_family(w1_ds2760_family);
-MODULE_LICENSE("GPL");
MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, "
"Matt Reimer <mreimer@vpop.net>, "
"Anton Vorontsov <cbou@mail.ru>");
-MODULE_DESCRIPTION("ds2760 battery driver");
+MODULE_DESCRIPTION("1-wire Driver Dallas 2760 battery monitor chip");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2760));
diff --git a/drivers/power/supply/generic-adc-battery.c b/drivers/power/supply/generic-adc-battery.c
index 28dc056eaafa..bc462d1ec963 100644
--- a/drivers/power/supply/generic-adc-battery.c
+++ b/drivers/power/supply/generic-adc-battery.c
@@ -241,10 +241,10 @@ static int gab_probe(struct platform_device *pdev)
struct power_supply_desc *psy_desc;
struct power_supply_config psy_cfg = {};
struct gab_platform_data *pdata = pdev->dev.platform_data;
- enum power_supply_property *properties;
int ret = 0;
int chan;
- int index = 0;
+ int index = ARRAY_SIZE(gab_props);
+ bool any = false;
adc_bat = devm_kzalloc(&pdev->dev, sizeof(*adc_bat), GFP_KERNEL);
if (!adc_bat) {
@@ -278,8 +278,6 @@ static int gab_probe(struct platform_device *pdev)
}
memcpy(psy_desc->properties, gab_props, sizeof(gab_props));
- properties = (enum power_supply_property *)
- ((char *)psy_desc->properties + sizeof(gab_props));
/*
* getting channel from iio and copying the battery properties
@@ -293,15 +291,22 @@ static int gab_probe(struct platform_device *pdev)
adc_bat->channel[chan] = NULL;
} else {
/* copying properties for supported channels only */
- memcpy(properties + sizeof(*(psy_desc->properties)) * index,
- &gab_dyn_props[chan],
- sizeof(gab_dyn_props[chan]));
- index++;
+ int index2;
+
+ for (index2 = 0; index2 < index; index2++) {
+ if (psy_desc->properties[index2] ==
+ gab_dyn_props[chan])
+ break; /* already known */
+ }
+ if (index2 == index) /* really new */
+ psy_desc->properties[index++] =
+ gab_dyn_props[chan];
+ any = true;
}
}
/* none of the channels are supported so let's bail out */
- if (index == 0) {
+ if (!any) {
ret = -ENODEV;
goto second_mem_fail;
}
@@ -312,7 +317,7 @@ static int gab_probe(struct platform_device *pdev)
* as come channels may be not be supported by the device.So
* we need to take care of that.
*/
- psy_desc->num_properties = ARRAY_SIZE(gab_props) + index;
+ psy_desc->num_properties = index;
adc_bat->psy = power_supply_register(&pdev->dev, psy_desc, &psy_cfg);
if (IS_ERR(adc_bat->psy)) {
diff --git a/drivers/power/supply/lego_ev3_battery.c b/drivers/power/supply/lego_ev3_battery.c
index 7b993d669f7f..1ae3710909b7 100644
--- a/drivers/power/supply/lego_ev3_battery.c
+++ b/drivers/power/supply/lego_ev3_battery.c
@@ -39,7 +39,7 @@ static int lego_ev3_battery_get_property(struct power_supply *psy,
union power_supply_propval *val)
{
struct lego_ev3_battery *batt = power_supply_get_drvdata(psy);
- int val2;
+ int ret, val2;
switch (psp) {
case POWER_SUPPLY_PROP_TECHNOLOGY:
@@ -47,11 +47,18 @@ static int lego_ev3_battery_get_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
/* battery voltage is iio channel * 2 + Vce of transistor */
- iio_read_channel_processed(batt->iio_v, &val->intval);
+ ret = iio_read_channel_processed(batt->iio_v, &val->intval);
+ if (ret)
+ return ret;
+
val->intval *= 2000;
- val->intval += 200000;
+ val->intval += 50000;
+
/* plus adjust for shunt resistor drop */
- iio_read_channel_processed(batt->iio_i, &val2);
+ ret = iio_read_channel_processed(batt->iio_i, &val2);
+ if (ret)
+ return ret;
+
val2 *= 1000;
val2 /= 15;
val->intval += val2;
@@ -64,7 +71,10 @@ static int lego_ev3_battery_get_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
/* battery current is iio channel / 15 / 0.05 ohms */
- iio_read_channel_processed(batt->iio_i, &val->intval);
+ ret = iio_read_channel_processed(batt->iio_i, &val->intval);
+ if (ret)
+ return ret;
+
val->intval *= 20000;
val->intval /= 15;
break;
diff --git a/drivers/power/supply/max1721x_battery.c b/drivers/power/supply/max1721x_battery.c
index 9ee601a03d9b..9ca895b0dabb 100644
--- a/drivers/power/supply/max1721x_battery.c
+++ b/drivers/power/supply/max1721x_battery.c
@@ -372,7 +372,7 @@ static int devm_w1_max1721x_add_device(struct w1_slave *sl)
}
if (!info->rsense) {
- dev_warn(info->w1_dev, "RSenese not calibrated, set 10 mOhms!\n");
+ dev_warn(info->w1_dev, "RSense not calibrated, set 10 mOhms!\n");
info->rsense = 1000; /* in regs in 10^-5 */
}
dev_info(info->w1_dev, "RSense: %d mOhms.\n", info->rsense / 100);
diff --git a/drivers/power/supply/max77693_charger.c b/drivers/power/supply/max77693_charger.c
index 6c78884bad5e..749c7926e3c9 100644
--- a/drivers/power/supply/max77693_charger.c
+++ b/drivers/power/supply/max77693_charger.c
@@ -567,6 +567,7 @@ static int max77693_set_charge_input_threshold_volt(struct max77693_charger *chg
case 4800000:
case 4900000:
data = (uvolt - 4700000) / 100000;
+ break;
default:
dev_err(chg->dev, "Wrong value for charge input voltage regulation threshold\n");
return -EINVAL;
diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
index d21f478741c1..e85361878450 100644
--- a/drivers/power/supply/power_supply_core.c
+++ b/drivers/power/supply/power_supply_core.c
@@ -14,6 +14,7 @@
#include <linux/types.h>
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/notifier.h>
#include <linux/err.h>
@@ -140,8 +141,13 @@ static void power_supply_deferred_register_work(struct work_struct *work)
struct power_supply *psy = container_of(work, struct power_supply,
deferred_register_work.work);
- if (psy->dev.parent)
- mutex_lock(&psy->dev.parent->mutex);
+ if (psy->dev.parent) {
+ while (!mutex_trylock(&psy->dev.parent->mutex)) {
+ if (psy->removing)
+ return;
+ msleep(10);
+ }
+ }
power_supply_changed(psy);
@@ -1082,6 +1088,7 @@ EXPORT_SYMBOL_GPL(devm_power_supply_register_no_ws);
void power_supply_unregister(struct power_supply *psy)
{
WARN_ON(atomic_dec_return(&psy->use_cnt));
+ psy->removing = true;
cancel_work_sync(&psy->changed_work);
cancel_delayed_work_sync(&psy->deferred_register_work);
sysfs_remove_link(&psy->dev.kobj, "powers");
diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c
index 83d7b4115857..8ba6abf584de 100644
--- a/drivers/power/supply/sbs-battery.c
+++ b/drivers/power/supply/sbs-battery.c
@@ -23,6 +23,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/power/sbs-battery.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
@@ -156,6 +157,9 @@ static enum power_supply_property sbs_properties[] = {
POWER_SUPPLY_PROP_MODEL_NAME
};
+/* Supports special manufacturer commands from TI BQ20Z75 IC. */
+#define SBS_FLAGS_TI_BQ20Z75 BIT(0)
+
struct sbs_info {
struct i2c_client *client;
struct power_supply *power_supply;
@@ -168,6 +172,7 @@ struct sbs_info {
u32 poll_retry_count;
struct delayed_work work;
struct mutex mode_lock;
+ u32 flags;
};
static char model_name[I2C_SMBUS_BLOCK_MAX + 1];
@@ -316,16 +321,40 @@ static int sbs_get_battery_presence_and_health(
struct i2c_client *client, enum power_supply_property psp,
union power_supply_propval *val)
{
+ int ret;
+
+ if (psp == POWER_SUPPLY_PROP_PRESENT) {
+ /* Dummy command; if it succeeds, battery is present. */
+ ret = sbs_read_word_data(client, sbs_data[REG_STATUS].addr);
+ if (ret < 0)
+ val->intval = 0; /* battery disconnected */
+ else
+ val->intval = 1; /* battery present */
+ } else { /* POWER_SUPPLY_PROP_HEALTH */
+ /* SBS spec doesn't have a general health command. */
+ val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
+ }
+
+ return 0;
+}
+
+static int sbs_get_ti_battery_presence_and_health(
+ struct i2c_client *client, enum power_supply_property psp,
+ union power_supply_propval *val)
+{
s32 ret;
/*
* Write to ManufacturerAccess with ManufacturerAccess command
- * and then read the status. Do not check for error on the write
- * since not all batteries implement write access to this command,
- * while others mandate it.
+ * and then read the status.
*/
- sbs_write_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr,
- MANUFACTURER_ACCESS_STATUS);
+ ret = sbs_write_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr,
+ MANUFACTURER_ACCESS_STATUS);
+ if (ret < 0) {
+ if (psp == POWER_SUPPLY_PROP_PRESENT)
+ val->intval = 0; /* battery removed */
+ return ret;
+ }
ret = sbs_read_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr);
if (ret < 0) {
@@ -600,7 +629,12 @@ static int sbs_get_property(struct power_supply *psy,
switch (psp) {
case POWER_SUPPLY_PROP_PRESENT:
case POWER_SUPPLY_PROP_HEALTH:
- ret = sbs_get_battery_presence_and_health(client, psp, val);
+ if (client->flags & SBS_FLAGS_TI_BQ20Z75)
+ ret = sbs_get_ti_battery_presence_and_health(client,
+ psp, val);
+ else
+ ret = sbs_get_battery_presence_and_health(client, psp,
+ val);
if (psp == POWER_SUPPLY_PROP_PRESENT)
return 0;
break;
@@ -806,6 +840,7 @@ static int sbs_probe(struct i2c_client *client,
if (!chip)
return -ENOMEM;
+ chip->flags = (u32)(uintptr_t)of_device_get_match_data(&client->dev);
chip->client = client;
chip->enable_detection = false;
psy_cfg.of_node = client->dev.of_node;
@@ -911,16 +946,19 @@ static int sbs_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct sbs_info *chip = i2c_get_clientdata(client);
+ int ret;
if (chip->poll_time > 0)
cancel_delayed_work_sync(&chip->work);
- /*
- * Write to manufacturer access with sleep command.
- * Support is manufacturer dependend, so ignore errors.
- */
- sbs_write_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr,
- MANUFACTURER_ACCESS_SLEEP);
+ if (chip->flags & SBS_FLAGS_TI_BQ20Z75) {
+ /* Write to manufacturer access with sleep command. */
+ ret = sbs_write_word_data(client,
+ sbs_data[REG_MANUFACTURER_DATA].addr,
+ MANUFACTURER_ACCESS_SLEEP);
+ if (chip->is_present && ret < 0)
+ return ret;
+ }
return 0;
}
@@ -941,7 +979,10 @@ MODULE_DEVICE_TABLE(i2c, sbs_id);
static const struct of_device_id sbs_dt_ids[] = {
{ .compatible = "sbs,sbs-battery" },
- { .compatible = "ti,bq20z75" },
+ {
+ .compatible = "ti,bq20z75",
+ .data = (void *)SBS_FLAGS_TI_BQ20Z75,
+ },
{ }
};
MODULE_DEVICE_TABLE(of, sbs_dt_ids);
diff --git a/drivers/power/supply/tps65217_charger.c b/drivers/power/supply/tps65217_charger.c
index 1f5234098aaf..814c2b81fdfe 100644
--- a/drivers/power/supply/tps65217_charger.c
+++ b/drivers/power/supply/tps65217_charger.c
@@ -1,20 +1,8 @@
-/*
- * Battery charger driver for TI's tps65217
- *
- * Copyright (c) 2015, Collabora Ltd.
-
- * 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.
-
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-License-Identifier: GPL-2.0
+// Battery charger driver for TI's tps65217
+//
+// Copyright (C) 2015 Collabora Ltd.
+// Author: Enric Balletbo i Serra <enric.balletbo@collabora.com>
/*
* Battery charger driver for TI's tps65217
diff --git a/drivers/power/supply/wm8350_power.c b/drivers/power/supply/wm8350_power.c
index a2740cf57ad3..15c0ca15e2aa 100644
--- a/drivers/power/supply/wm8350_power.c
+++ b/drivers/power/supply/wm8350_power.c
@@ -230,7 +230,8 @@ static irqreturn_t wm8350_charger_handler(int irq, void *data)
case WM8350_IRQ_EXT_USB_FB:
case WM8350_IRQ_EXT_WALL_FB:
wm8350_charger_config(wm8350, policy);
- case WM8350_IRQ_EXT_BAT_FB: /* Fall through */
+ /* Fall through */
+ case WM8350_IRQ_EXT_BAT_FB:
power_supply_changed(power->battery);
power_supply_changed(power->usb);
power_supply_changed(power->ac);
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index 6364890575ec..06ed20dd01ba 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -348,7 +348,7 @@ static void send_sig_all(int sig)
if (is_global_init(p))
continue;
- do_send_sig_info(sig, SEND_SIG_FORCED, p, true);
+ do_send_sig_info(sig, SEND_SIG_FORCED, p, PIDTYPE_MAX);
}
read_unlock(&tasklist_lock);
}
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 11c2df904ac9..32bc3e3fe4d3 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -2113,7 +2113,7 @@ static int __tty_fasync(int fd, struct file *filp, int on)
type = PIDTYPE_PGID;
} else {
pid = task_pid(current);
- type = PIDTYPE_PID;
+ type = PIDTYPE_TGID;
}
get_pid(pid);
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig
index 7931231d8e80..e22fdeddada1 100644
--- a/drivers/w1/slaves/Kconfig
+++ b/drivers/w1/slaves/Kconfig
@@ -100,18 +100,6 @@ config W1_SLAVE_DS2438
Say Y here if you want to use a 1-wire
DS2438 Smart Battery Monitor device support
-config W1_SLAVE_DS2760
- tristate "Dallas 2760 battery monitor chip (HP iPAQ & others)"
- help
- If you enable this you will have the DS2760 battery monitor
- chip support.
-
- The battery monitor chip is used in many batteries/devices
- as the one who is responsible for charging/discharging/monitoring
- Li+ batteries.
-
- If you are unsure, say N.
-
config W1_SLAVE_DS2780
tristate "Dallas 2780 battery monitor chip"
help
diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile
index d5f4f4d5b9e5..eab29f151413 100644
--- a/drivers/w1/slaves/Makefile
+++ b/drivers/w1/slaves/Makefile
@@ -14,7 +14,6 @@ obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o
obj-$(CONFIG_W1_SLAVE_DS2805) += w1_ds2805.o
obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o
obj-$(CONFIG_W1_SLAVE_DS2438) += w1_ds2438.o
-obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o
obj-$(CONFIG_W1_SLAVE_DS2780) += w1_ds2780.o
obj-$(CONFIG_W1_SLAVE_DS2781) += w1_ds2781.o
obj-$(CONFIG_W1_SLAVE_DS28E04) += w1_ds28e04.o
diff --git a/drivers/w1/slaves/w1_ds2760.c b/drivers/w1/slaves/w1_ds2760.c
deleted file mode 100644
index 26168abfb8b8..000000000000
--- a/drivers/w1/slaves/w1_ds2760.c
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * 1-Wire implementation for the ds2760 chip
- *
- * Copyright © 2004-2005, Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>
- *
- * Use consistent with the GNU GPL is permitted,
- * provided that this copyright notice is
- * preserved in its entirety in all copies and derived works.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/types.h>
-#include <linux/platform_device.h>
-#include <linux/mutex.h>
-#include <linux/idr.h>
-#include <linux/gfp.h>
-
-#include <linux/w1.h>
-
-#include "w1_ds2760.h"
-
-#define W1_FAMILY_DS2760 0x30
-
-static int w1_ds2760_io(struct device *dev, char *buf, int addr, size_t count,
- int io)
-{
- struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
-
- if (!dev)
- return 0;
-
- mutex_lock(&sl->master->bus_mutex);
-
- if (addr > DS2760_DATA_SIZE || addr < 0) {
- count = 0;
- goto out;
- }
- if (addr + count > DS2760_DATA_SIZE)
- count = DS2760_DATA_SIZE - addr;
-
- if (!w1_reset_select_slave(sl)) {
- if (!io) {
- w1_write_8(sl->master, W1_DS2760_READ_DATA);
- w1_write_8(sl->master, addr);
- count = w1_read_block(sl->master, buf, count);
- } else {
- w1_write_8(sl->master, W1_DS2760_WRITE_DATA);
- w1_write_8(sl->master, addr);
- w1_write_block(sl->master, buf, count);
- /* XXX w1_write_block returns void, not n_written */
- }
- }
-
-out:
- mutex_unlock(&sl->master->bus_mutex);
-
- return count;
-}
-
-int w1_ds2760_read(struct device *dev, char *buf, int addr, size_t count)
-{
- return w1_ds2760_io(dev, buf, addr, count, 0);
-}
-EXPORT_SYMBOL(w1_ds2760_read);
-
-int w1_ds2760_write(struct device *dev, char *buf, int addr, size_t count)
-{
- return w1_ds2760_io(dev, buf, addr, count, 1);
-}
-EXPORT_SYMBOL(w1_ds2760_write);
-
-static int w1_ds2760_eeprom_cmd(struct device *dev, int addr, int cmd)
-{
- struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
-
- if (!dev)
- return -EINVAL;
-
- mutex_lock(&sl->master->bus_mutex);
-
- if (w1_reset_select_slave(sl) == 0) {
- w1_write_8(sl->master, cmd);
- w1_write_8(sl->master, addr);
- }
-
- mutex_unlock(&sl->master->bus_mutex);
- return 0;
-}
-
-int w1_ds2760_store_eeprom(struct device *dev, int addr)
-{
- return w1_ds2760_eeprom_cmd(dev, addr, W1_DS2760_COPY_DATA);
-}
-EXPORT_SYMBOL(w1_ds2760_store_eeprom);
-
-int w1_ds2760_recall_eeprom(struct device *dev, int addr)
-{
- return w1_ds2760_eeprom_cmd(dev, addr, W1_DS2760_RECALL_DATA);
-}
-EXPORT_SYMBOL(w1_ds2760_recall_eeprom);
-
-static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
- loff_t off, size_t count)
-{
- struct device *dev = container_of(kobj, struct device, kobj);
- return w1_ds2760_read(dev, buf, off, count);
-}
-
-static BIN_ATTR_RO(w1_slave, DS2760_DATA_SIZE);
-
-static struct bin_attribute *w1_ds2760_bin_attrs[] = {
- &bin_attr_w1_slave,
- NULL,
-};
-
-static const struct attribute_group w1_ds2760_group = {
- .bin_attrs = w1_ds2760_bin_attrs,
-};
-
-static const struct attribute_group *w1_ds2760_groups[] = {
- &w1_ds2760_group,
- NULL,
-};
-
-static int w1_ds2760_add_slave(struct w1_slave *sl)
-{
- int ret;
- struct platform_device *pdev;
-
- pdev = platform_device_alloc("ds2760-battery", PLATFORM_DEVID_AUTO);
- if (!pdev)
- return -ENOMEM;
- pdev->dev.parent = &sl->dev;
-
- ret = platform_device_add(pdev);
- if (ret)
- goto pdev_add_failed;
-
- dev_set_drvdata(&sl->dev, pdev);
-
- return 0;
-
-pdev_add_failed:
- platform_device_put(pdev);
-
- return ret;
-}
-
-static void w1_ds2760_remove_slave(struct w1_slave *sl)
-{
- struct platform_device *pdev = dev_get_drvdata(&sl->dev);
-
- platform_device_unregister(pdev);
-}
-
-static struct w1_family_ops w1_ds2760_fops = {
- .add_slave = w1_ds2760_add_slave,
- .remove_slave = w1_ds2760_remove_slave,
- .groups = w1_ds2760_groups,
-};
-
-static struct w1_family w1_ds2760_family = {
- .fid = W1_FAMILY_DS2760,
- .fops = &w1_ds2760_fops,
-};
-module_w1_family(w1_ds2760_family);
-
-MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>");
-MODULE_DESCRIPTION("1-wire Driver Dallas 2760 battery monitor chip");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2760));
diff --git a/drivers/w1/slaves/w1_ds2760.h b/drivers/w1/slaves/w1_ds2760.h
deleted file mode 100644
index 24168c94eeae..000000000000
--- a/drivers/w1/slaves/w1_ds2760.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 1-Wire implementation for the ds2760 chip
- *
- * Copyright © 2004-2005, Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>
- *
- * Use consistent with the GNU GPL is permitted,
- * provided that this copyright notice is
- * preserved in its entirety in all copies and derived works.
- *
- */
-
-#ifndef __w1_ds2760_h__
-#define __w1_ds2760_h__
-
-/* Known commands to the DS2760 chip */
-#define W1_DS2760_SWAP 0xAA
-#define W1_DS2760_READ_DATA 0x69
-#define W1_DS2760_WRITE_DATA 0x6C
-#define W1_DS2760_COPY_DATA 0x48
-#define W1_DS2760_RECALL_DATA 0xB8
-#define W1_DS2760_LOCK 0x6A
-
-/* Number of valid register addresses */
-#define DS2760_DATA_SIZE 0x40
-
-#define DS2760_PROTECTION_REG 0x00
-
-#define DS2760_STATUS_REG 0x01
-#define DS2760_STATUS_IE (1 << 2)
-#define DS2760_STATUS_SWEN (1 << 3)
-#define DS2760_STATUS_RNAOP (1 << 4)
-#define DS2760_STATUS_PMOD (1 << 5)
-
-#define DS2760_EEPROM_REG 0x07
-#define DS2760_SPECIAL_FEATURE_REG 0x08
-#define DS2760_VOLTAGE_MSB 0x0c
-#define DS2760_VOLTAGE_LSB 0x0d
-#define DS2760_CURRENT_MSB 0x0e
-#define DS2760_CURRENT_LSB 0x0f
-#define DS2760_CURRENT_ACCUM_MSB 0x10
-#define DS2760_CURRENT_ACCUM_LSB 0x11
-#define DS2760_TEMP_MSB 0x18
-#define DS2760_TEMP_LSB 0x19
-#define DS2760_EEPROM_BLOCK0 0x20
-#define DS2760_ACTIVE_FULL 0x20
-#define DS2760_EEPROM_BLOCK1 0x30
-#define DS2760_STATUS_WRITE_REG 0x31
-#define DS2760_RATED_CAPACITY 0x32
-#define DS2760_CURRENT_OFFSET_BIAS 0x33
-#define DS2760_ACTIVE_EMPTY 0x3b
-
-extern int w1_ds2760_read(struct device *dev, char *buf, int addr,
- size_t count);
-extern int w1_ds2760_write(struct device *dev, char *buf, int addr,
- size_t count);
-extern int w1_ds2760_store_eeprom(struct device *dev, int addr);
-extern int w1_ds2760_recall_eeprom(struct device *dev, int addr);
-
-#endif /* !__w1_ds2760_h__ */
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index caef0e0fd817..890c038c25f8 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -26,6 +26,7 @@
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/hwmon.h>
+#include <linux/of.h>
#include <linux/atomic.h>
@@ -686,6 +687,8 @@ static int __w1_attach_slave_device(struct w1_slave *sl)
sl->dev.bus = &w1_bus_type;
sl->dev.release = &w1_slave_release;
sl->dev.groups = w1_slave_groups;
+ sl->dev.of_node = of_find_matching_node(sl->master->dev.of_node,
+ sl->family->of_match_table);
dev_set_name(&sl->dev, "%02x-%012llx",
(unsigned int) sl->reg_num.family,