mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-07 08:48:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
238
drivers/muic/Kconfig
Normal file
238
drivers/muic/Kconfig
Normal file
|
@ -0,0 +1,238 @@
|
|||
#
|
||||
# MUIC devices
|
||||
#
|
||||
|
||||
comment "MUIC configs"
|
||||
|
||||
config USE_MUIC
|
||||
bool "Using MUIC device driver"
|
||||
depends on I2C
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for
|
||||
the MUIC device driver.
|
||||
|
||||
config USE_SAFEOUT
|
||||
bool "Using SAFEOUT device driver"
|
||||
depends on I2C
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for
|
||||
the SAFEOUT device driver.
|
||||
|
||||
config MUIC_NOTIFIER
|
||||
bool "MUIC notifier support"
|
||||
depends on USE_MUIC
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for
|
||||
the MUIC attached device status change notification.
|
||||
|
||||
config SAMSUNG_MUIC
|
||||
bool "Using samsung MUIC chip driver"
|
||||
depends on USE_MUIC
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for the MUIC chip.
|
||||
|
||||
config MUIC_ADCMODE_SWITCH_WA
|
||||
bool "Using MUIC chip ADC Mode change W/A"
|
||||
depends on USE_MUIC
|
||||
default n
|
||||
help
|
||||
This feature is for MUIC chip's ADC Mode change
|
||||
chip bug Workaround.
|
||||
|
||||
config MUIC_RUSTPROOF_ON_USER
|
||||
bool "Using MUIC chip Rustproof function ON for User binary"
|
||||
depends on USE_MUIC
|
||||
default n
|
||||
help
|
||||
This feature is for MUIC chip's Rustproof function.
|
||||
|
||||
config MUIC_FSA9480
|
||||
tristate "FSA9480 USB Switch"
|
||||
depends on I2C
|
||||
help
|
||||
The FSA9480 is a USB port accessory detector and switch.
|
||||
The FSA9480 is fully controlled using I2C and enables USB data,
|
||||
stereo and mono audio, video, microphone and UART data to use
|
||||
a common connector port.
|
||||
|
||||
config MUIC_MAX77804
|
||||
bool "Using MAX77804 MUIC"
|
||||
depends on MFD_MAX77804 && USE_MUIC
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for
|
||||
for the MAX77804 MUIC chip.
|
||||
|
||||
config MUIC_MAX77804K
|
||||
bool "Using MAX77804K MUIC"
|
||||
depends on MFD_MAX77804K && USE_MUIC
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for
|
||||
for the MAX77804K MUIC chip.
|
||||
|
||||
config MUIC_MAX77828
|
||||
bool "Using MAX77828 MUIC"
|
||||
depends on MFD_MAX77828 && USE_MUIC
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for
|
||||
for the MAX77828 MUIC chip.
|
||||
|
||||
config MUIC_MAX77843
|
||||
bool "Using MAX77843 MUIC"
|
||||
depends on MFD_MAX77843 && USE_MUIC
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for
|
||||
for the MAX77843 MUIC chip.
|
||||
|
||||
config HV_MUIC_MAX77843_AFC
|
||||
bool "Using MAX77843 AFC MUIC"
|
||||
depends on MUIC_MAX77843
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for the MAX77843 AFC MUIC.
|
||||
|
||||
config MUIC_MAX77843_IGNORE_ADCERR_WA
|
||||
bool "Using MAX77843 MUIC ignore ADCERR WA"
|
||||
depends on MUIC_MAX77843
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for the MAX77843 MUIC RESET WA.
|
||||
|
||||
config MUIC_MAX77843_RESET_WA
|
||||
bool "Using MAX77843 MUIC RESET WA"
|
||||
depends on MUIC_MAX77843
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for the MAX77843 MUIC RESET WA.
|
||||
|
||||
config MUIC_MAX77833
|
||||
bool "Using MAX77833 MUIC"
|
||||
depends on MFD_MAX77833 && USE_MUIC
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for
|
||||
for the MAX77833 MUIC chip.
|
||||
|
||||
config HV_MUIC_MAX77833_AFC
|
||||
bool "Using MAX77833 AFC MUIC"
|
||||
depends on MUIC_MAX77833
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for the MAX77833 AFC MUIC.
|
||||
|
||||
config MUIC_MAX77833_IGNORE_ADCERR_WA
|
||||
bool "Using MAX77833 MUIC ignore ADCERR WA"
|
||||
depends on MUIC_MAX77833
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for the MAX77833 MUIC RESET WA.
|
||||
|
||||
config MUIC_MAX77833_RESET_WA
|
||||
bool "Using MAX77833 MUIC RESET WA"
|
||||
depends on MUIC_MAX77833
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for the MAX77833 MUIC RESET WA.
|
||||
|
||||
config MUIC_MAX77888
|
||||
bool "Using MAX77888 MUIC"
|
||||
depends on MFD_MAX77888 && USE_MUIC
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for
|
||||
for the MAX77888 MUIC chip.
|
||||
|
||||
config MUIC_S2MM001
|
||||
bool "Using S2MM001 MUIC"
|
||||
depends on USE_MUIC
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for
|
||||
for the S2MM001 MUIC chip.
|
||||
|
||||
config MUIC_SM5703
|
||||
bool "SM5703 MUIC"
|
||||
depends on USE_MUIC
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for the SM5703 MUIC chip.
|
||||
|
||||
config MUIC_SM5705
|
||||
bool "SM5705 MUIC"
|
||||
depends on USE_MUIC
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for the SM5705 MUIC chip.
|
||||
|
||||
config MUIC_S2MU005
|
||||
bool "S2MU005 MUIC"
|
||||
depends on USE_MUIC
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for the S2MU005 MUIC chip.
|
||||
|
||||
config MUIC_RT8973
|
||||
bool "Using RT8973 MUIC"
|
||||
depends on USE_MUIC
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for
|
||||
the RT8973 MUIC chip.
|
||||
|
||||
config MUIC_SM5504
|
||||
bool "Using SM5504 MUIC"
|
||||
depends on USE_MUIC
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for
|
||||
the SM5504 MUIC chip.
|
||||
|
||||
config MUIC_SM5705_AFC
|
||||
bool "SM5705 AFC MUIC"
|
||||
depends on MUIC_SM5705
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for the SM5705 AFC MUIC.
|
||||
|
||||
config MUIC_UNIVERSAL
|
||||
bool "UNIVERSAL MUIC"
|
||||
depends on USE_MUIC
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for various MUIC chips.
|
||||
|
||||
config MUIC_UNIVERSAL_SM5703
|
||||
bool "SM5703 MUIC"
|
||||
depends on USE_MUIC
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for the SM5703 MUIC chip.
|
||||
|
||||
config MUIC_UNIVERSAL_SM5705
|
||||
bool "SM5705 MUIC"
|
||||
depends on USE_MUIC
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for the SM5705 MUIC chip.
|
||||
|
||||
config MUIC_UNIVERSAL_SM5705_AFC
|
||||
bool "SM5705 MUIC AFC"
|
||||
depends on USE_MUIC
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for the SM5705 MUIC
|
||||
AFC feature.
|
||||
|
||||
config MUIC_UNIVERSAL_SM5504
|
||||
bool "SM5504 MUIC"
|
||||
depends on USE_MUIC
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for the SM5504 MUIC chip.
|
38
drivers/muic/Makefile
Normal file
38
drivers/muic/Makefile
Normal file
|
@ -0,0 +1,38 @@
|
|||
#
|
||||
# Makefile for muic devices
|
||||
#
|
||||
|
||||
obj-y += muic-core.o
|
||||
obj-$(CONFIG_MUIC_NOTIFIER) += muic_notifier.o
|
||||
obj-$(CONFIG_MUIC_MAX77804) += max77804-muic.o
|
||||
obj-$(CONFIG_MUIC_MAX77804K) += max77804k-muic.o
|
||||
obj-$(CONFIG_MUIC_MAX77828) += max77828-muic.o
|
||||
obj-$(CONFIG_MUIC_MAX77843) += max77843-muic.o
|
||||
obj-$(CONFIG_HV_MUIC_MAX77843_AFC) += max77843-muic-afc.o
|
||||
obj-$(CONFIG_MUIC_MAX77833) += max77833-muic.o
|
||||
obj-$(CONFIG_HV_MUIC_MAX77833_AFC) += max77833-muic-afc.o
|
||||
obj-$(CONFIG_MUIC_MAX77888) += max77888-muic.o
|
||||
obj-$(CONFIG_MUIC_FSA9480) += fsa9480.o
|
||||
obj-$(CONFIG_MUIC_S2MM001) += s2mm001.o
|
||||
obj-$(CONFIG_MUIC_SM5703) += sm5703-muic.o
|
||||
obj-$(CONFIG_MUIC_SM5705) += sm5703-muic.o
|
||||
obj-$(CONFIG_MUIC_SM5705_AFC) += sm5703-muic-afc.o
|
||||
obj-$(CONFIG_MUIC_S2MU005) += s2mu005-muic.o
|
||||
obj-$(CONFIG_MUIC_RT8973) += rt8973.o
|
||||
obj-$(CONFIG_MUIC_SM5504) += sm5504.o
|
||||
|
||||
# MUIC_UNIVERSAL
|
||||
obj-$(CONFIG_MUIC_UNIVERSAL) += universal/muic_task.o
|
||||
obj-$(CONFIG_MUIC_UNIVERSAL) += universal/muic_state.o
|
||||
obj-$(CONFIG_MUIC_UNIVERSAL) += universal/muic_apis.o
|
||||
obj-$(CONFIG_MUIC_UNIVERSAL) += universal/muic_sysfs.o
|
||||
obj-$(CONFIG_MUIC_UNIVERSAL) += universal/muic_debug.o
|
||||
obj-$(CONFIG_MUIC_UNIVERSAL) += universal/muic_dt.o
|
||||
obj-$(CONFIG_MUIC_UNIVERSAL) += universal/muic_i2c.o
|
||||
obj-$(CONFIG_MUIC_UNIVERSAL) += universal/muic_regmap.o
|
||||
obj-$(CONFIG_MUIC_UNIVERSAL) += universal/muic_vps.o
|
||||
obj-$(CONFIG_MUIC_UNIVERSAL_SM5703) += universal/muic_regmap_sm5703.o
|
||||
obj-$(CONFIG_MUIC_UNIVERSAL_SM5705) += universal/muic_regmap_sm5705.o
|
||||
obj-$(CONFIG_MUIC_UNIVERSAL_SM5705_AFC) += universal/muic_afc.o
|
||||
obj-$(CONFIG_MUIC_UNIVERSAL_SM5504) += universal/muic_regmap_sm5504.o
|
||||
|
549
drivers/muic/fsa9480.c
Normal file
549
drivers/muic/fsa9480.c
Normal file
|
@ -0,0 +1,549 @@
|
|||
/*
|
||||
* fsa9480.c - FSA9480 micro USB switch device driver
|
||||
*
|
||||
* Copyright (C) 2010 Samsung Electronics
|
||||
* Minkyu Kang <mk7.kang@samsung.com>
|
||||
* Wonguk Jeong <wonguk.jeong@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_data/fsa9480.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
/* FSA9480 I2C registers */
|
||||
#define FSA9480_REG_DEVID 0x01
|
||||
#define FSA9480_REG_CTRL 0x02
|
||||
#define FSA9480_REG_INT1 0x03
|
||||
#define FSA9480_REG_INT2 0x04
|
||||
#define FSA9480_REG_INT1_MASK 0x05
|
||||
#define FSA9480_REG_INT2_MASK 0x06
|
||||
#define FSA9480_REG_ADC 0x07
|
||||
#define FSA9480_REG_TIMING1 0x08
|
||||
#define FSA9480_REG_TIMING2 0x09
|
||||
#define FSA9480_REG_DEV_T1 0x0a
|
||||
#define FSA9480_REG_DEV_T2 0x0b
|
||||
#define FSA9480_REG_BTN1 0x0c
|
||||
#define FSA9480_REG_BTN2 0x0d
|
||||
#define FSA9480_REG_CK 0x0e
|
||||
#define FSA9480_REG_CK_INT1 0x0f
|
||||
#define FSA9480_REG_CK_INT2 0x10
|
||||
#define FSA9480_REG_CK_INTMASK1 0x11
|
||||
#define FSA9480_REG_CK_INTMASK2 0x12
|
||||
#define FSA9480_REG_MANSW1 0x13
|
||||
#define FSA9480_REG_MANSW2 0x14
|
||||
|
||||
/* Control */
|
||||
#define CON_SWITCH_OPEN (1 << 4)
|
||||
#define CON_RAW_DATA (1 << 3)
|
||||
#define CON_MANUAL_SW (1 << 2)
|
||||
#define CON_WAIT (1 << 1)
|
||||
#define CON_INT_MASK (1 << 0)
|
||||
#define CON_MASK (CON_SWITCH_OPEN | CON_RAW_DATA | \
|
||||
CON_MANUAL_SW | CON_WAIT)
|
||||
|
||||
/* Device Type 1 */
|
||||
#define DEV_USB_OTG (1 << 7)
|
||||
#define DEV_DEDICATED_CHG (1 << 6)
|
||||
#define DEV_USB_CHG (1 << 5)
|
||||
#define DEV_CAR_KIT (1 << 4)
|
||||
#define DEV_UART (1 << 3)
|
||||
#define DEV_USB (1 << 2)
|
||||
#define DEV_AUDIO_2 (1 << 1)
|
||||
#define DEV_AUDIO_1 (1 << 0)
|
||||
|
||||
#define DEV_T1_USB_MASK (DEV_USB_OTG | DEV_USB)
|
||||
#define DEV_T1_UART_MASK (DEV_UART)
|
||||
#define DEV_T1_CHARGER_MASK (DEV_DEDICATED_CHG | DEV_USB_CHG)
|
||||
|
||||
/* Device Type 2 */
|
||||
#define DEV_AV (1 << 6)
|
||||
#define DEV_TTY (1 << 5)
|
||||
#define DEV_PPD (1 << 4)
|
||||
#define DEV_JIG_UART_OFF (1 << 3)
|
||||
#define DEV_JIG_UART_ON (1 << 2)
|
||||
#define DEV_JIG_USB_OFF (1 << 1)
|
||||
#define DEV_JIG_USB_ON (1 << 0)
|
||||
|
||||
#define DEV_T2_USB_MASK (DEV_JIG_USB_OFF | DEV_JIG_USB_ON)
|
||||
#define DEV_T2_UART_MASK (DEV_JIG_UART_OFF | DEV_JIG_UART_ON)
|
||||
#define DEV_T2_JIG_MASK (DEV_JIG_USB_OFF | DEV_JIG_USB_ON | \
|
||||
DEV_JIG_UART_OFF | DEV_JIG_UART_ON)
|
||||
|
||||
/*
|
||||
* Manual Switch
|
||||
* D- [7:5] / D+ [4:2]
|
||||
* 000: Open all / 001: USB / 010: AUDIO / 011: UART / 100: V_AUDIO
|
||||
*/
|
||||
#define SW_VAUDIO ((4 << 5) | (4 << 2))
|
||||
#define SW_UART ((3 << 5) | (3 << 2))
|
||||
#define SW_AUDIO ((2 << 5) | (2 << 2))
|
||||
#define SW_DHOST ((1 << 5) | (1 << 2))
|
||||
#define SW_AUTO ((0 << 5) | (0 << 2))
|
||||
|
||||
/* Interrupt 1 */
|
||||
#define INT_DETACH (1 << 1)
|
||||
#define INT_ATTACH (1 << 0)
|
||||
|
||||
struct fsa9480_usbsw {
|
||||
struct i2c_client *client;
|
||||
struct fsa9480_platform_data *pdata;
|
||||
int dev1;
|
||||
int dev2;
|
||||
int mansw;
|
||||
};
|
||||
|
||||
static struct fsa9480_usbsw *chip;
|
||||
|
||||
static int fsa9480_write_reg(struct i2c_client *client,
|
||||
int reg, int value)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, reg, value);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "%s: err %d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsa9480_read_reg(struct i2c_client *client, int reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, reg);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "%s: err %d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsa9480_read_irq(struct i2c_client *client, int *value)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(client,
|
||||
FSA9480_REG_INT1, 2, (u8 *)value);
|
||||
*value &= 0xffff;
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "%s: err %d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void fsa9480_set_switch(const char *buf)
|
||||
{
|
||||
struct fsa9480_usbsw *usbsw = chip;
|
||||
struct i2c_client *client = usbsw->client;
|
||||
unsigned int value;
|
||||
unsigned int path = 0;
|
||||
|
||||
value = fsa9480_read_reg(client, FSA9480_REG_CTRL);
|
||||
|
||||
if (!strncmp(buf, "VAUDIO", 6)) {
|
||||
path = SW_VAUDIO;
|
||||
value &= ~CON_MANUAL_SW;
|
||||
} else if (!strncmp(buf, "UART", 4)) {
|
||||
path = SW_UART;
|
||||
value &= ~CON_MANUAL_SW;
|
||||
} else if (!strncmp(buf, "AUDIO", 5)) {
|
||||
path = SW_AUDIO;
|
||||
value &= ~CON_MANUAL_SW;
|
||||
} else if (!strncmp(buf, "DHOST", 5)) {
|
||||
path = SW_DHOST;
|
||||
value &= ~CON_MANUAL_SW;
|
||||
} else if (!strncmp(buf, "AUTO", 4)) {
|
||||
path = SW_AUTO;
|
||||
value |= CON_MANUAL_SW;
|
||||
} else {
|
||||
printk(KERN_ERR "Wrong command\n");
|
||||
return;
|
||||
}
|
||||
|
||||
usbsw->mansw = path;
|
||||
fsa9480_write_reg(client, FSA9480_REG_MANSW1, path);
|
||||
fsa9480_write_reg(client, FSA9480_REG_CTRL, value);
|
||||
}
|
||||
|
||||
static ssize_t fsa9480_get_switch(char *buf)
|
||||
{
|
||||
struct fsa9480_usbsw *usbsw = chip;
|
||||
struct i2c_client *client = usbsw->client;
|
||||
unsigned int value;
|
||||
|
||||
value = fsa9480_read_reg(client, FSA9480_REG_MANSW1);
|
||||
|
||||
if (value == SW_VAUDIO)
|
||||
return sprintf(buf, "VAUDIO\n");
|
||||
else if (value == SW_UART)
|
||||
return sprintf(buf, "UART\n");
|
||||
else if (value == SW_AUDIO)
|
||||
return sprintf(buf, "AUDIO\n");
|
||||
else if (value == SW_DHOST)
|
||||
return sprintf(buf, "DHOST\n");
|
||||
else if (value == SW_AUTO)
|
||||
return sprintf(buf, "AUTO\n");
|
||||
else
|
||||
return sprintf(buf, "%x", value);
|
||||
}
|
||||
|
||||
static ssize_t fsa9480_show_device(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct fsa9480_usbsw *usbsw = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = usbsw->client;
|
||||
int dev1, dev2;
|
||||
|
||||
dev1 = fsa9480_read_reg(client, FSA9480_REG_DEV_T1);
|
||||
dev2 = fsa9480_read_reg(client, FSA9480_REG_DEV_T2);
|
||||
|
||||
if (!dev1 && !dev2)
|
||||
return sprintf(buf, "NONE\n");
|
||||
|
||||
/* USB */
|
||||
if (dev1 & DEV_T1_USB_MASK || dev2 & DEV_T2_USB_MASK)
|
||||
return sprintf(buf, "USB\n");
|
||||
|
||||
/* UART */
|
||||
if (dev1 & DEV_T1_UART_MASK || dev2 & DEV_T2_UART_MASK)
|
||||
return sprintf(buf, "UART\n");
|
||||
|
||||
/* CHARGER */
|
||||
if (dev1 & DEV_T1_CHARGER_MASK)
|
||||
return sprintf(buf, "CHARGER\n");
|
||||
|
||||
/* JIG */
|
||||
if (dev2 & DEV_T2_JIG_MASK)
|
||||
return sprintf(buf, "JIG\n");
|
||||
|
||||
return sprintf(buf, "UNKNOWN\n");
|
||||
}
|
||||
|
||||
static ssize_t fsa9480_show_manualsw(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return fsa9480_get_switch(buf);
|
||||
|
||||
}
|
||||
|
||||
static ssize_t fsa9480_set_manualsw(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
fsa9480_set_switch(buf);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(device, S_IRUGO, fsa9480_show_device, NULL);
|
||||
static DEVICE_ATTR(switch, S_IRUGO | S_IWUSR,
|
||||
fsa9480_show_manualsw, fsa9480_set_manualsw);
|
||||
|
||||
static struct attribute *fsa9480_attributes[] = {
|
||||
&dev_attr_device.attr,
|
||||
&dev_attr_switch.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group fsa9480_group = {
|
||||
.attrs = fsa9480_attributes,
|
||||
};
|
||||
|
||||
static void fsa9480_detect_dev(struct fsa9480_usbsw *usbsw, int intr)
|
||||
{
|
||||
int val1, val2, ctrl;
|
||||
struct fsa9480_platform_data *pdata = usbsw->pdata;
|
||||
struct i2c_client *client = usbsw->client;
|
||||
|
||||
val1 = fsa9480_read_reg(client, FSA9480_REG_DEV_T1);
|
||||
val2 = fsa9480_read_reg(client, FSA9480_REG_DEV_T2);
|
||||
ctrl = fsa9480_read_reg(client, FSA9480_REG_CTRL);
|
||||
|
||||
dev_info(&client->dev, "intr: 0x%x, dev1: 0x%x, dev2: 0x%x\n",
|
||||
intr, val1, val2);
|
||||
|
||||
if (!intr)
|
||||
goto out;
|
||||
|
||||
if (intr & INT_ATTACH) { /* Attached */
|
||||
/* USB */
|
||||
if (val1 & DEV_T1_USB_MASK || val2 & DEV_T2_USB_MASK) {
|
||||
if (pdata->usb_cb)
|
||||
pdata->usb_cb(FSA9480_ATTACHED);
|
||||
|
||||
if (usbsw->mansw) {
|
||||
fsa9480_write_reg(client,
|
||||
FSA9480_REG_MANSW1, usbsw->mansw);
|
||||
}
|
||||
}
|
||||
|
||||
/* UART */
|
||||
if (val1 & DEV_T1_UART_MASK || val2 & DEV_T2_UART_MASK) {
|
||||
if (pdata->uart_cb)
|
||||
pdata->uart_cb(FSA9480_ATTACHED);
|
||||
|
||||
if (!(ctrl & CON_MANUAL_SW)) {
|
||||
fsa9480_write_reg(client,
|
||||
FSA9480_REG_MANSW1, SW_UART);
|
||||
}
|
||||
}
|
||||
|
||||
/* CHARGER */
|
||||
if (val1 & DEV_T1_CHARGER_MASK) {
|
||||
if (pdata->charger_cb)
|
||||
pdata->charger_cb(FSA9480_ATTACHED);
|
||||
}
|
||||
|
||||
/* JIG */
|
||||
if (val2 & DEV_T2_JIG_MASK) {
|
||||
if (pdata->jig_cb)
|
||||
pdata->jig_cb(FSA9480_ATTACHED);
|
||||
}
|
||||
} else if (intr & INT_DETACH) { /* Detached */
|
||||
/* USB */
|
||||
if (usbsw->dev1 & DEV_T1_USB_MASK ||
|
||||
usbsw->dev2 & DEV_T2_USB_MASK) {
|
||||
if (pdata->usb_cb)
|
||||
pdata->usb_cb(FSA9480_DETACHED);
|
||||
}
|
||||
|
||||
/* UART */
|
||||
if (usbsw->dev1 & DEV_T1_UART_MASK ||
|
||||
usbsw->dev2 & DEV_T2_UART_MASK) {
|
||||
if (pdata->uart_cb)
|
||||
pdata->uart_cb(FSA9480_DETACHED);
|
||||
}
|
||||
|
||||
/* CHARGER */
|
||||
if (usbsw->dev1 & DEV_T1_CHARGER_MASK) {
|
||||
if (pdata->charger_cb)
|
||||
pdata->charger_cb(FSA9480_DETACHED);
|
||||
}
|
||||
|
||||
/* JIG */
|
||||
if (usbsw->dev2 & DEV_T2_JIG_MASK) {
|
||||
if (pdata->jig_cb)
|
||||
pdata->jig_cb(FSA9480_DETACHED);
|
||||
}
|
||||
}
|
||||
|
||||
usbsw->dev1 = val1;
|
||||
usbsw->dev2 = val2;
|
||||
|
||||
out:
|
||||
ctrl &= ~CON_INT_MASK;
|
||||
fsa9480_write_reg(client, FSA9480_REG_CTRL, ctrl);
|
||||
}
|
||||
|
||||
static irqreturn_t fsa9480_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct fsa9480_usbsw *usbsw = data;
|
||||
struct i2c_client *client = usbsw->client;
|
||||
int intr;
|
||||
|
||||
/* clear interrupt */
|
||||
fsa9480_read_irq(client, &intr);
|
||||
|
||||
/* device detection */
|
||||
fsa9480_detect_dev(usbsw, intr);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int fsa9480_irq_init(struct fsa9480_usbsw *usbsw)
|
||||
{
|
||||
struct fsa9480_platform_data *pdata = usbsw->pdata;
|
||||
struct i2c_client *client = usbsw->client;
|
||||
int ret;
|
||||
int intr;
|
||||
unsigned int ctrl = CON_MASK;
|
||||
|
||||
/* clear interrupt */
|
||||
fsa9480_read_irq(client, &intr);
|
||||
|
||||
/* unmask interrupt (attach/detach only) */
|
||||
fsa9480_write_reg(client, FSA9480_REG_INT1_MASK, 0xfc);
|
||||
fsa9480_write_reg(client, FSA9480_REG_INT2_MASK, 0x1f);
|
||||
|
||||
usbsw->mansw = fsa9480_read_reg(client, FSA9480_REG_MANSW1);
|
||||
|
||||
if (usbsw->mansw)
|
||||
ctrl &= ~CON_MANUAL_SW; /* Manual Switching Mode */
|
||||
|
||||
fsa9480_write_reg(client, FSA9480_REG_CTRL, ctrl);
|
||||
|
||||
if (pdata && pdata->cfg_gpio)
|
||||
pdata->cfg_gpio();
|
||||
|
||||
if (client->irq) {
|
||||
ret = request_threaded_irq(client->irq, NULL,
|
||||
fsa9480_irq_handler,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"fsa9480 micro USB", usbsw);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to reqeust IRQ\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (pdata)
|
||||
device_init_wakeup(&client->dev, pdata->wakeup);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsa9480_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct fsa9480_usbsw *usbsw;
|
||||
int ret = 0;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
return -EIO;
|
||||
|
||||
usbsw = kzalloc(sizeof(struct fsa9480_usbsw), GFP_KERNEL);
|
||||
if (!usbsw) {
|
||||
dev_err(&client->dev, "failed to allocate driver data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
usbsw->client = client;
|
||||
usbsw->pdata = client->dev.platform_data;
|
||||
|
||||
chip = usbsw;
|
||||
|
||||
i2c_set_clientdata(client, usbsw);
|
||||
|
||||
ret = fsa9480_irq_init(usbsw);
|
||||
if (ret)
|
||||
goto fail1;
|
||||
|
||||
ret = sysfs_create_group(&client->dev.kobj, &fsa9480_group);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"failed to create fsa9480 attribute group\n");
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
/* ADC Detect Time: 500ms */
|
||||
fsa9480_write_reg(client, FSA9480_REG_TIMING1, 0x6);
|
||||
|
||||
if (chip->pdata->reset_cb)
|
||||
chip->pdata->reset_cb();
|
||||
|
||||
/* device detection */
|
||||
fsa9480_detect_dev(usbsw, INT_ATTACH);
|
||||
|
||||
pm_runtime_set_active(&client->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
if (client->irq)
|
||||
free_irq(client->irq, usbsw);
|
||||
fail1:
|
||||
kfree(usbsw);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsa9480_remove(struct i2c_client *client)
|
||||
{
|
||||
struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);
|
||||
if (client->irq)
|
||||
free_irq(client->irq, usbsw);
|
||||
|
||||
sysfs_remove_group(&client->dev.kobj, &fsa9480_group);
|
||||
device_init_wakeup(&client->dev, 0);
|
||||
kfree(usbsw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int fsa9480_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);
|
||||
struct fsa9480_platform_data *pdata = usbsw->pdata;
|
||||
|
||||
if (device_may_wakeup(&client->dev) && client->irq)
|
||||
enable_irq_wake(client->irq);
|
||||
|
||||
if (pdata->usb_power)
|
||||
pdata->usb_power(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsa9480_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);
|
||||
int dev1, dev2;
|
||||
|
||||
if (device_may_wakeup(&client->dev) && client->irq)
|
||||
disable_irq_wake(client->irq);
|
||||
|
||||
/*
|
||||
* Clear Pending interrupt. Note that detect_dev does what
|
||||
* the interrupt handler does. So, we don't miss pending and
|
||||
* we reenable interrupt if there is one.
|
||||
*/
|
||||
fsa9480_read_reg(client, FSA9480_REG_INT1);
|
||||
fsa9480_read_reg(client, FSA9480_REG_INT2);
|
||||
|
||||
dev1 = fsa9480_read_reg(client, FSA9480_REG_DEV_T1);
|
||||
dev2 = fsa9480_read_reg(client, FSA9480_REG_DEV_T2);
|
||||
|
||||
/* device detection */
|
||||
fsa9480_detect_dev(usbsw, (dev1 || dev2) ? INT_ATTACH : INT_DETACH);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(fsa9480_pm_ops, fsa9480_suspend, fsa9480_resume);
|
||||
#define FSA9480_PM_OPS (&fsa9480_pm_ops)
|
||||
|
||||
#else
|
||||
|
||||
#define FSA9480_PM_OPS NULL
|
||||
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct i2c_device_id fsa9480_id[] = {
|
||||
{"fsa9480", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, fsa9480_id);
|
||||
|
||||
static struct i2c_driver fsa9480_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "fsa9480",
|
||||
.pm = FSA9480_PM_OPS,
|
||||
},
|
||||
.probe = fsa9480_probe,
|
||||
.remove = fsa9480_remove,
|
||||
.id_table = fsa9480_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(fsa9480_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
|
||||
MODULE_DESCRIPTION("FSA9480 USB Switch driver");
|
||||
MODULE_LICENSE("GPL");
|
1674
drivers/muic/max77804-muic.c
Normal file
1674
drivers/muic/max77804-muic.c
Normal file
File diff suppressed because it is too large
Load diff
2238
drivers/muic/max77804k-muic.c
Normal file
2238
drivers/muic/max77804k-muic.c
Normal file
File diff suppressed because it is too large
Load diff
2045
drivers/muic/max77828-muic.c
Normal file
2045
drivers/muic/max77828-muic.c
Normal file
File diff suppressed because it is too large
Load diff
256
drivers/muic/max77833-muic-afc.c
Normal file
256
drivers/muic/max77833-muic-afc.c
Normal file
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
* max77833-muic.c - MUIC driver for the Maxim 77833
|
||||
*
|
||||
* Copyright (C) 2015 Samsung Electronics
|
||||
* Insun Choi <insun77.choi@samsung.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <linux/mfd/max77833.h>
|
||||
#include <linux/mfd/max77833-private.h>
|
||||
|
||||
/* MUIC header file */
|
||||
#include <linux/muic/muic.h>
|
||||
#include <linux/muic/max77833-muic.h>
|
||||
#include <linux/muic/max77833-muic-hv.h>
|
||||
|
||||
#if defined(CONFIG_MUIC_NOTIFIER)
|
||||
#include <linux/muic/muic_notifier.h>
|
||||
#endif /* CONFIG_MUIC_NOTIFIER */
|
||||
|
||||
void max77833_muic_hv_qc_autoset(struct max77833_muic_data *muic_data, u8 val, u8 mask)
|
||||
{
|
||||
cmd_queue_t *cmd_queue = &(muic_data->muic_cmd_queue);
|
||||
muic_cmd_data cmd_data;
|
||||
u8 opcode = COMMAND_QC_AUTOSET_WRITE;
|
||||
u8 reg = MAX77833_MUIC_REG_DAT_IN1;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
init_muic_cmd_data(&cmd_data);
|
||||
|
||||
cmd_data.opcode = opcode;
|
||||
cmd_data.reg = reg;
|
||||
cmd_data.val = val;
|
||||
cmd_data.mask = mask;
|
||||
|
||||
enqueue_muic_cmd(cmd_queue, cmd_data);
|
||||
|
||||
init_muic_cmd_data(&cmd_data);
|
||||
cmd_data.response = opcode;
|
||||
|
||||
enqueue_muic_cmd(cmd_queue, cmd_data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void max77833_muic_hv_qc_enable(struct max77833_muic_data *muic_data)
|
||||
{
|
||||
cmd_queue_t *cmd_queue = &(muic_data->muic_cmd_queue);
|
||||
muic_cmd_data cmd_data;
|
||||
u8 opcode = COMMAND_QC_ENABLE_READ;
|
||||
u8 reg = MAX77833_MUIC_REG_DAT_OUT1;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
init_muic_cmd_data(&cmd_data);
|
||||
cmd_data.opcode = opcode;
|
||||
|
||||
enqueue_muic_cmd(cmd_queue, cmd_data);
|
||||
|
||||
init_muic_cmd_data(&cmd_data);
|
||||
cmd_data.response = opcode;
|
||||
cmd_data.reg = reg;
|
||||
|
||||
enqueue_muic_cmd(cmd_queue, cmd_data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void max77833_muic_hv_qc_disable(struct max77833_muic_data *muic_data)
|
||||
{
|
||||
cmd_queue_t *cmd_queue = &(muic_data->muic_cmd_queue);
|
||||
muic_cmd_data cmd_data;
|
||||
u8 opcode = COMMAND_QC_DISABLE_READ;
|
||||
u8 reg = MAX77833_MUIC_REG_DAT_OUT1;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
init_muic_cmd_data(&cmd_data);
|
||||
cmd_data.opcode = opcode;
|
||||
|
||||
enqueue_muic_cmd(cmd_queue, cmd_data);
|
||||
|
||||
init_muic_cmd_data(&cmd_data);
|
||||
cmd_data.response = opcode;
|
||||
cmd_data.reg = reg;
|
||||
|
||||
enqueue_muic_cmd(cmd_queue, cmd_data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void max77833_muic_hv_chgin_read(struct max77833_muic_data *muic_data)
|
||||
{
|
||||
cmd_queue_t *cmd_queue = &(muic_data->muic_cmd_queue);
|
||||
muic_cmd_data cmd_data;
|
||||
u8 opcode = COMMAND_CHGIN_READ;
|
||||
u8 reg = MAX77833_MUIC_REG_DAT_OUT1;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
init_muic_cmd_data(&cmd_data);
|
||||
cmd_data.opcode = opcode;
|
||||
|
||||
enqueue_muic_cmd(cmd_queue, cmd_data);
|
||||
|
||||
init_muic_cmd_data(&cmd_data);
|
||||
cmd_data.response = opcode;
|
||||
cmd_data.reg = reg;
|
||||
|
||||
enqueue_muic_cmd(cmd_queue, cmd_data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void max77833_muic_hv_fchv_capa_read(struct max77833_muic_data *muic_data)
|
||||
{
|
||||
cmd_queue_t *cmd_queue = &(muic_data->muic_cmd_queue);
|
||||
muic_cmd_data cmd_data;
|
||||
u8 opcode = COMMAND_AFC_CAPA_READ;
|
||||
u8 reg = MAX77833_MUIC_REG_DAT_OUT1;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
init_muic_cmd_data(&cmd_data);
|
||||
cmd_data.opcode = opcode;
|
||||
|
||||
enqueue_muic_cmd(cmd_queue, cmd_data);
|
||||
|
||||
init_muic_cmd_data(&cmd_data);
|
||||
cmd_data.response = opcode;
|
||||
cmd_data.reg = reg;
|
||||
|
||||
enqueue_muic_cmd(cmd_queue, cmd_data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void max77833_muic_hv_fchv_set(struct max77833_muic_data *muic_data, u8 val, u8 mask)
|
||||
{
|
||||
cmd_queue_t *cmd_queue = &(muic_data->muic_cmd_queue);
|
||||
muic_cmd_data cmd_data;
|
||||
u8 opcode = COMMAND_AFC_SET_WRITE;
|
||||
u8 reg = MAX77833_MUIC_REG_DAT_IN1;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
init_muic_cmd_data(&cmd_data);
|
||||
cmd_data.opcode = opcode;
|
||||
cmd_data.reg = reg;
|
||||
cmd_data.val = val;
|
||||
cmd_data.mask = mask;
|
||||
|
||||
enqueue_muic_cmd(cmd_queue, cmd_data);
|
||||
|
||||
init_muic_cmd_data(&cmd_data);
|
||||
cmd_data.response = opcode;
|
||||
|
||||
enqueue_muic_cmd(cmd_queue, cmd_data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void max77833_muic_hv_fchv_enable(struct max77833_muic_data *muic_data, u8 val, u8 mask)
|
||||
{
|
||||
cmd_queue_t *cmd_queue = &(muic_data->muic_cmd_queue);
|
||||
muic_cmd_data cmd_data;
|
||||
u8 opcode = COMMAND_AFC_ENABLE_READ;
|
||||
u8 reg = MAX77833_MUIC_REG_DAT_IN1;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
init_muic_cmd_data(&cmd_data);
|
||||
cmd_data.opcode = opcode;
|
||||
cmd_data.reg = reg;
|
||||
cmd_data.val = val;
|
||||
cmd_data.mask = mask;
|
||||
|
||||
enqueue_muic_cmd(cmd_queue, cmd_data);
|
||||
|
||||
init_muic_cmd_data(&cmd_data);
|
||||
cmd_data.response = opcode;
|
||||
|
||||
enqueue_muic_cmd(cmd_queue, cmd_data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void max77833_muic_hv_fchv_disable(struct max77833_muic_data *muic_data)
|
||||
{
|
||||
cmd_queue_t *cmd_queue = &(muic_data->muic_cmd_queue);
|
||||
muic_cmd_data cmd_data;
|
||||
u8 opcode = COMMAND_AFC_DISABLE_READ;
|
||||
u8 reg = MAX77833_MUIC_REG_DAT_OUT1;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
init_muic_cmd_data(&cmd_data);
|
||||
cmd_data.opcode = opcode;
|
||||
|
||||
enqueue_muic_cmd(cmd_queue, cmd_data);
|
||||
|
||||
init_muic_cmd_data(&cmd_data);
|
||||
cmd_data.response = opcode;
|
||||
cmd_data.reg = reg;
|
||||
|
||||
enqueue_muic_cmd(cmd_queue, cmd_data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void max77833_muic_set_afc_ready(struct max77833_muic_data *muic_data, bool value)
|
||||
{
|
||||
bool before, after;
|
||||
|
||||
before = muic_data->is_check_hv;
|
||||
muic_data->is_check_hv = value;
|
||||
after = muic_data->is_check_hv;
|
||||
|
||||
pr_info("%s:%s check_hv[%d->%d]\n", MUIC_DEV_NAME, __func__, before, after);
|
||||
}
|
||||
|
||||
void max77833_muic_hv_fchv_disable_set(struct max77833_muic_data *muic_data)
|
||||
{
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
max77833_muic_set_afc_ready(muic_data, false);
|
||||
max77833_muic_hv_fchv_disable(muic_data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void max77833_muic_hv_qc_disable_set(struct max77833_muic_data *muic_data)
|
||||
{
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
max77833_muic_set_afc_ready(muic_data, false);
|
||||
max77833_muic_hv_qc_disable(muic_data);
|
||||
|
||||
return;
|
||||
}
|
3001
drivers/muic/max77833-muic.c
Normal file
3001
drivers/muic/max77833-muic.c
Normal file
File diff suppressed because it is too large
Load diff
1707
drivers/muic/max77843-muic-afc.c
Normal file
1707
drivers/muic/max77843-muic-afc.c
Normal file
File diff suppressed because it is too large
Load diff
2809
drivers/muic/max77843-muic.c
Normal file
2809
drivers/muic/max77843-muic.c
Normal file
File diff suppressed because it is too large
Load diff
1800
drivers/muic/max77888-muic.c
Normal file
1800
drivers/muic/max77888-muic.c
Normal file
File diff suppressed because it is too large
Load diff
364
drivers/muic/muic-core.c
Normal file
364
drivers/muic/muic-core.c
Normal file
|
@ -0,0 +1,364 @@
|
|||
/* drivers/muic/muic-core.c
|
||||
*
|
||||
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-gpio.h>
|
||||
#include <linux/gpio.h>
|
||||
//#include <mach/irqs.h>
|
||||
|
||||
/* switch device header */
|
||||
#ifdef CONFIG_SWITCH
|
||||
#include <linux/switch.h>
|
||||
#endif /* CONFIG_SWITCH */
|
||||
|
||||
#if defined(CONFIG_USE_SAFEOUT)
|
||||
#include <linux/regulator/consumer.h>
|
||||
#endif
|
||||
|
||||
#include <linux/muic/muic.h>
|
||||
|
||||
#if defined(CONFIG_MUIC_NOTIFIER)
|
||||
#include <linux/muic/muic_notifier.h>
|
||||
#endif /* CONFIG_MUIC_NOTIFIER */
|
||||
|
||||
#ifdef CONFIG_SWITCH
|
||||
static struct switch_dev switch_dock = {
|
||||
.name = "dock",
|
||||
};
|
||||
#endif /* CONFIG_SWITCH */
|
||||
|
||||
/* 1: 619K is used as a wake-up noti which sends a dock noti.
|
||||
* 0: 619K is used 619K itself, JIG_UART_ON
|
||||
*/
|
||||
int muic_wakeup_noti = 1;
|
||||
|
||||
/* don't access this variable directly!! except get_switch_sel_value function.
|
||||
* you must get switch_sel value by using get_switch_sel function. */
|
||||
static int switch_sel;
|
||||
|
||||
/*
|
||||
* func : set_switch_sel
|
||||
* switch_sel value get from bootloader comand line
|
||||
* switch_sel data consist 12 bits (xxxxyyyyzzzz)
|
||||
* first 4bits(zzzz) mean path infomation.
|
||||
* next 4bits(yyyy) mean if pmic version info
|
||||
* next 4bits(xxxx) mean afc disable info
|
||||
*/
|
||||
static int set_switch_sel(char *str)
|
||||
{
|
||||
get_option(&str, &switch_sel);
|
||||
switch_sel = switch_sel & 0x0fff;
|
||||
printk(KERN_DEBUG "[muic] %s : 0x%x\n",
|
||||
__func__, switch_sel);
|
||||
|
||||
return switch_sel;
|
||||
}
|
||||
__setup("pmic_info=", set_switch_sel);
|
||||
|
||||
int get_switch_sel(void)
|
||||
{
|
||||
return switch_sel;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MUIC_NOTIFIER)
|
||||
static struct notifier_block dock_notifier_block;
|
||||
|
||||
static void muic_send_dock_intent(int type)
|
||||
{
|
||||
printk(KERN_DEBUG "[muic] %s: MUIC dock type(%d)\n", __func__, type);
|
||||
#ifdef CONFIG_SWITCH
|
||||
switch_set_state(&switch_dock, type);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int muic_dock_attach_notify(int type, const char *name)
|
||||
{
|
||||
printk(KERN_DEBUG "[muic] %s: %s\n", __func__, name);
|
||||
muic_send_dock_intent(type);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int muic_dock_detach_notify(void)
|
||||
{
|
||||
printk(KERN_DEBUG "[muic] %s\n", __func__);
|
||||
muic_send_dock_intent(MUIC_DOCK_DETACHED);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
/* Notice:
|
||||
* Define your own wake-up Noti. function to use 619K
|
||||
* as a different purpose as it is for wake-up by default.
|
||||
*/
|
||||
static void __muic_set_wakeup_noti(int flag)
|
||||
{
|
||||
pr_info("%s: %d but 1 by default\n", __func__, flag);
|
||||
muic_wakeup_noti = 1;
|
||||
}
|
||||
void muic_set_wakeup_noti(int flag)
|
||||
__attribute__((weak, alias("__muic_set_wakeup_noti")));
|
||||
|
||||
static int muic_handle_dock_notification(struct notifier_block *nb,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
muic_attached_dev_t attached_dev = *(muic_attached_dev_t *)data;
|
||||
int type = MUIC_DOCK_DETACHED;
|
||||
const char *name;
|
||||
|
||||
if (attached_dev == ATTACHED_DEV_JIG_UART_ON_MUIC) {
|
||||
if (muic_wakeup_noti) {
|
||||
|
||||
muic_set_wakeup_noti(0);
|
||||
|
||||
if (action == MUIC_NOTIFY_CMD_ATTACH) {
|
||||
type = MUIC_DOCK_DESKDOCK;
|
||||
name = "Desk Dock Attach";
|
||||
return muic_dock_attach_notify(type, name);
|
||||
}
|
||||
else if (action == MUIC_NOTIFY_CMD_DETACH)
|
||||
return muic_dock_detach_notify();
|
||||
}
|
||||
printk(KERN_DEBUG "[muic] %s: ignore(%d)\n", __func__, attached_dev);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
switch (attached_dev) {
|
||||
case ATTACHED_DEV_DESKDOCK_MUIC:
|
||||
case ATTACHED_DEV_DESKDOCK_VB_MUIC:
|
||||
if (action == MUIC_NOTIFY_CMD_ATTACH) {
|
||||
type = MUIC_DOCK_DESKDOCK;
|
||||
name = "Desk Dock Attach";
|
||||
return muic_dock_attach_notify(type, name);
|
||||
}
|
||||
else if (action == MUIC_NOTIFY_CMD_DETACH)
|
||||
return muic_dock_detach_notify();
|
||||
break;
|
||||
case ATTACHED_DEV_CARDOCK_MUIC:
|
||||
if (action == MUIC_NOTIFY_CMD_ATTACH) {
|
||||
type = MUIC_DOCK_CARDOCK;
|
||||
name = "Car Dock Attach";
|
||||
return muic_dock_attach_notify(type, name);
|
||||
}
|
||||
else if (action == MUIC_NOTIFY_CMD_DETACH)
|
||||
return muic_dock_detach_notify();
|
||||
break;
|
||||
case ATTACHED_DEV_SMARTDOCK_MUIC:
|
||||
case ATTACHED_DEV_SMARTDOCK_VB_MUIC:
|
||||
case ATTACHED_DEV_SMARTDOCK_TA_MUIC:
|
||||
case ATTACHED_DEV_SMARTDOCK_USB_MUIC:
|
||||
if (action == MUIC_NOTIFY_CMD_LOGICALLY_ATTACH) {
|
||||
type = MUIC_DOCK_SMARTDOCK;
|
||||
name = "Smart Dock Attach";
|
||||
return muic_dock_attach_notify(type, name);
|
||||
}
|
||||
else if (action == MUIC_NOTIFY_CMD_LOGICALLY_DETACH)
|
||||
return muic_dock_detach_notify();
|
||||
break;
|
||||
case ATTACHED_DEV_UNIVERSAL_MMDOCK_MUIC:
|
||||
if (action == MUIC_NOTIFY_CMD_ATTACH) {
|
||||
type = MUIC_DOCK_SMARTDOCK;
|
||||
name = "Universal MMDock Attach";
|
||||
return muic_dock_attach_notify(type, name);
|
||||
}
|
||||
else if (action == MUIC_NOTIFY_CMD_DETACH)
|
||||
return muic_dock_detach_notify();
|
||||
break;
|
||||
case ATTACHED_DEV_AUDIODOCK_MUIC:
|
||||
if (action == MUIC_NOTIFY_CMD_ATTACH) {
|
||||
type = MUIC_DOCK_AUDIODOCK;
|
||||
name = "Audio Dock Attach";
|
||||
return muic_dock_attach_notify(type, name);
|
||||
}
|
||||
else if (action == MUIC_NOTIFY_CMD_DETACH)
|
||||
return muic_dock_detach_notify();
|
||||
break;
|
||||
case ATTACHED_DEV_HMT_MUIC:
|
||||
if (action == MUIC_NOTIFY_CMD_ATTACH) {
|
||||
type = MUIC_DOCK_HMT;
|
||||
name = "HMT Attach";
|
||||
return muic_dock_attach_notify(type, name);
|
||||
}
|
||||
else if (action == MUIC_NOTIFY_CMD_DETACH)
|
||||
return muic_dock_detach_notify();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "[muic] %s: ignore(%d)\n", __func__, attached_dev);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
#endif /* CONFIG_MUIC_NOTIFIER */
|
||||
|
||||
#if defined(CONFIG_USE_SAFEOUT)
|
||||
int muic_set_safeout(int safeout_path)
|
||||
{
|
||||
struct regulator *regulator;
|
||||
int ret;
|
||||
|
||||
printk(KERN_DEBUG "[muic] %s:MUIC safeout path=%d\n", __func__, safeout_path);
|
||||
|
||||
if (safeout_path == MUIC_PATH_USB_CP) {
|
||||
regulator = regulator_get(NULL, "safeout1_range");
|
||||
if (IS_ERR(regulator) || regulator == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
if (regulator_is_enabled(regulator))
|
||||
regulator_force_disable(regulator);
|
||||
regulator_put(regulator);
|
||||
|
||||
regulator = regulator_get(NULL, "safeout2_range");
|
||||
if (IS_ERR(regulator) || regulator == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
if (!regulator_is_enabled(regulator)) {
|
||||
ret = regulator_enable(regulator);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
regulator_put(regulator);
|
||||
} else if (safeout_path == MUIC_PATH_USB_AP) {
|
||||
regulator = regulator_get(NULL, "safeout1_range");
|
||||
if (IS_ERR(regulator) || regulator == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
if (!regulator_is_enabled(regulator)) {
|
||||
ret = regulator_enable(regulator);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
regulator_put(regulator);
|
||||
|
||||
regulator = regulator_get(NULL, "safeout2_range");
|
||||
if (IS_ERR(regulator) || regulator == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
if (regulator_is_enabled(regulator))
|
||||
regulator_force_disable(regulator);
|
||||
regulator_put(regulator);
|
||||
} else {
|
||||
printk(KERN_DEBUG "[muic] %s: not control safeout(%d)\n", __func__, safeout_path);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
printk(KERN_DEBUG "[muic] %s: cannot regulator_enable (%d)\n", __func__, ret);
|
||||
regulator_put(regulator);
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_USE_SAFEOUT */
|
||||
|
||||
static void muic_init_switch_dev_cb(void)
|
||||
{
|
||||
#ifdef CONFIG_SWITCH
|
||||
int ret;
|
||||
|
||||
/* for DockObserver */
|
||||
ret = switch_dev_register(&switch_dock);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "[muic] %s: Failed to register dock switch(%d)\n",
|
||||
__func__, ret);
|
||||
return;
|
||||
}
|
||||
#endif /* CONFIG_SWITCH */
|
||||
|
||||
#if defined(CONFIG_MUIC_NOTIFIER)
|
||||
muic_notifier_register(&dock_notifier_block,
|
||||
muic_handle_dock_notification, MUIC_NOTIFY_DEV_DOCK);
|
||||
#endif /* CONFIG_MUIC_NOTIFIER */
|
||||
|
||||
printk(KERN_DEBUG "[muic] %s: done\n", __func__);
|
||||
}
|
||||
|
||||
static void muic_cleanup_switch_dev_cb(void)
|
||||
{
|
||||
#if defined(CONFIG_MUIC_NOTIFIER)
|
||||
muic_notifier_unregister(&dock_notifier_block);
|
||||
#endif /* CONFIG_MUIC_NOTIFIER */
|
||||
|
||||
printk(KERN_DEBUG "[muic] %s: done\n", __func__);
|
||||
}
|
||||
|
||||
extern struct muic_platform_data muic_pdata;
|
||||
|
||||
bool is_muic_usb_path_ap_usb(void)
|
||||
{
|
||||
if (MUIC_PATH_USB_AP == muic_pdata.usb_path) {
|
||||
printk(KERN_DEBUG "[muic] %s: [%d]\n", __func__, muic_pdata.usb_path);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_muic_usb_path_cp_usb(void)
|
||||
{
|
||||
if (MUIC_PATH_USB_CP == muic_pdata.usb_path) {
|
||||
printk(KERN_DEBUG "[muic] %s: [%d]\n", __func__, muic_pdata.usb_path);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int muic_init_gpio_cb(void)
|
||||
{
|
||||
struct muic_platform_data *pdata = &muic_pdata;
|
||||
const char *usb_mode;
|
||||
const char *uart_mode;
|
||||
int ret = 0;
|
||||
|
||||
printk(KERN_DEBUG "[muic] %s (%d)\n", __func__, switch_sel);
|
||||
|
||||
if (switch_sel & SWITCH_SEL_USB_MASK) {
|
||||
pdata->usb_path = MUIC_PATH_USB_AP;
|
||||
usb_mode = "PDA";
|
||||
} else {
|
||||
pdata->usb_path = MUIC_PATH_USB_CP;
|
||||
usb_mode = "MODEM";
|
||||
}
|
||||
|
||||
if (pdata->set_gpio_usb_sel)
|
||||
ret = pdata->set_gpio_usb_sel(pdata->usb_path);
|
||||
|
||||
if (switch_sel & SWITCH_SEL_UART_MASK) {
|
||||
pdata->uart_path = MUIC_PATH_UART_AP;
|
||||
uart_mode = "AP";
|
||||
} else {
|
||||
pdata->uart_path = MUIC_PATH_UART_CP;
|
||||
uart_mode = "CP";
|
||||
}
|
||||
|
||||
if (pdata->set_gpio_uart_sel)
|
||||
ret = pdata->set_gpio_uart_sel(pdata->uart_path);
|
||||
|
||||
if (!(switch_sel & SWITCH_SEL_RUSTPROOF_MASK))
|
||||
pdata->rustproof_on = true;
|
||||
else
|
||||
pdata->rustproof_on = false;
|
||||
|
||||
|
||||
printk(KERN_DEBUG "[muic] %s: usb_path(%s), uart_path(%s), rustproof(%c)\n",
|
||||
__func__, usb_mode,
|
||||
uart_mode, (pdata->rustproof_on ? 'T' : 'F'));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct muic_platform_data muic_pdata = {
|
||||
.init_switch_dev_cb = muic_init_switch_dev_cb,
|
||||
.cleanup_switch_dev_cb = muic_cleanup_switch_dev_cb,
|
||||
.init_gpio_cb = muic_init_gpio_cb,
|
||||
#if defined(CONFIG_USE_SAFEOUT)
|
||||
.set_safeout = muic_set_safeout,
|
||||
#endif /* CONFIG_USE_SAFEOUT */
|
||||
};
|
158
drivers/muic/muic_notifier.c
Normal file
158
drivers/muic/muic_notifier.c
Normal file
|
@ -0,0 +1,158 @@
|
|||
#include <linux/device.h>
|
||||
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/muic/muic.h>
|
||||
#include <linux/muic/muic_notifier.h>
|
||||
#include <linux/sec_sysfs.h>
|
||||
|
||||
#define SET_MUIC_NOTIFIER_BLOCK(nb, fn, dev) do { \
|
||||
(nb)->notifier_call = (fn); \
|
||||
(nb)->priority = (dev); \
|
||||
} while (0)
|
||||
|
||||
#define DESTROY_MUIC_NOTIFIER_BLOCK(nb) \
|
||||
SET_MUIC_NOTIFIER_BLOCK(nb, NULL, -1)
|
||||
|
||||
static struct muic_notifier_struct muic_notifier;
|
||||
|
||||
struct device *switch_device;
|
||||
|
||||
int muic_notifier_register(struct notifier_block *nb, notifier_fn_t notifier,
|
||||
muic_notifier_device_t listener)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
printk(KERN_DEBUG "[muic] %s: listener=%d\n", __func__, listener);
|
||||
|
||||
SET_MUIC_NOTIFIER_BLOCK(nb, notifier, listener);
|
||||
ret = blocking_notifier_chain_register(&(muic_notifier.notifier_call_chain), nb);
|
||||
if (ret < 0)
|
||||
printk(KERN_ERR "[muic] notifier_chain_register error(%d)\n", ret);
|
||||
|
||||
/* current muic's attached_device status notify */
|
||||
nb->notifier_call(nb, muic_notifier.cmd,
|
||||
&(muic_notifier.attached_dev));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int muic_notifier_unregister(struct notifier_block *nb)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
printk(KERN_DEBUG "[muic] %s: listener=%d unregister\n", __func__, nb->priority);
|
||||
|
||||
ret = blocking_notifier_chain_unregister(&(muic_notifier.notifier_call_chain), nb);
|
||||
if (ret < 0)
|
||||
printk(KERN_ERR "[muic] notifier_chain_unregister error(%d)\n", ret);
|
||||
DESTROY_MUIC_NOTIFIER_BLOCK(nb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int muic_notifier_notify(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
printk(KERN_DEBUG "[muic] %s: CMD=%d, DATA=%d\n", __func__, muic_notifier.cmd,
|
||||
muic_notifier.attached_dev);
|
||||
|
||||
ret = blocking_notifier_call_chain(&(muic_notifier.notifier_call_chain),
|
||||
muic_notifier.cmd, &(muic_notifier.attached_dev));
|
||||
|
||||
switch (ret) {
|
||||
case NOTIFY_STOP_MASK:
|
||||
case NOTIFY_BAD:
|
||||
printk(KERN_ERR "[muic] %s: notify error occur(0x%x)\n", __func__, ret);
|
||||
break;
|
||||
case NOTIFY_DONE:
|
||||
case NOTIFY_OK:
|
||||
printk(KERN_DEBUG "[muic] %s: notify done(0x%x)\n", __func__, ret);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_DEBUG "[muic] %s: notify status unknown(0x%x)\n", __func__, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void muic_notifier_attach_attached_dev(muic_attached_dev_t new_dev)
|
||||
{
|
||||
printk(KERN_DEBUG "[muic] %s: (%d)\n", __func__, new_dev);
|
||||
|
||||
muic_notifier.cmd = MUIC_NOTIFY_CMD_ATTACH;
|
||||
muic_notifier.attached_dev = new_dev;
|
||||
|
||||
/* muic's attached_device attach broadcast */
|
||||
muic_notifier_notify();
|
||||
}
|
||||
|
||||
void muic_notifier_detach_attached_dev(muic_attached_dev_t cur_dev)
|
||||
{
|
||||
printk(KERN_DEBUG "[muic] %s: (%d)\n", __func__, cur_dev);
|
||||
|
||||
muic_notifier.cmd = MUIC_NOTIFY_CMD_DETACH;
|
||||
|
||||
if (muic_notifier.attached_dev != cur_dev)
|
||||
printk(KERN_DEBUG "[muic] %s: muic_notifier(%d) != muic_data(%d)\n",
|
||||
__func__, muic_notifier.attached_dev, cur_dev);
|
||||
|
||||
if (muic_notifier.attached_dev != ATTACHED_DEV_NONE_MUIC) {
|
||||
/* muic's attached_device detach broadcast */
|
||||
muic_notifier_notify();
|
||||
}
|
||||
|
||||
muic_notifier.attached_dev = ATTACHED_DEV_NONE_MUIC;
|
||||
}
|
||||
|
||||
void muic_notifier_logically_attach_attached_dev(muic_attached_dev_t new_dev)
|
||||
{
|
||||
printk(KERN_DEBUG "[muic] %s: (%d)\n", __func__, new_dev);
|
||||
|
||||
muic_notifier.cmd = MUIC_NOTIFY_CMD_LOGICALLY_ATTACH;
|
||||
muic_notifier.attached_dev = new_dev;
|
||||
|
||||
/* muic's attached_device attach broadcast */
|
||||
muic_notifier_notify();
|
||||
}
|
||||
|
||||
void muic_notifier_logically_detach_attached_dev(muic_attached_dev_t cur_dev)
|
||||
{
|
||||
printk(KERN_DEBUG "[muic] %s: (%d)\n", __func__, cur_dev);
|
||||
|
||||
muic_notifier.cmd = MUIC_NOTIFY_CMD_LOGICALLY_DETACH;
|
||||
muic_notifier.attached_dev = cur_dev;
|
||||
|
||||
/* muic's attached_device detach broadcast */
|
||||
muic_notifier_notify();
|
||||
|
||||
muic_notifier.attached_dev = ATTACHED_DEV_NONE_MUIC;
|
||||
}
|
||||
|
||||
static int __init muic_notifier_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
printk(KERN_DEBUG "[muic] %s\n", __func__);
|
||||
|
||||
#ifdef CONFIG_SEC_SYSFS
|
||||
switch_device = sec_device_create(NULL, "switch");
|
||||
if (IS_ERR(switch_device)) {
|
||||
printk(KERN_ERR "[muic] Failed to create device(switch)!\n");
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&(muic_notifier.notifier_call_chain));
|
||||
muic_notifier.cmd = MUIC_NOTIFY_CMD_DETACH;
|
||||
muic_notifier.attached_dev = ATTACHED_DEV_UNKNOWN_MUIC;
|
||||
|
||||
#ifdef CONFIG_SEC_SYSFS
|
||||
out:
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
device_initcall(muic_notifier_init);
|
||||
|
1473
drivers/muic/rt8973.c
Executable file
1473
drivers/muic/rt8973.c
Executable file
File diff suppressed because it is too large
Load diff
2155
drivers/muic/s2mm001.c
Normal file
2155
drivers/muic/s2mm001.c
Normal file
File diff suppressed because it is too large
Load diff
2445
drivers/muic/s2mu005-muic.c
Normal file
2445
drivers/muic/s2mu005-muic.c
Normal file
File diff suppressed because it is too large
Load diff
1055
drivers/muic/sm5504.c
Normal file
1055
drivers/muic/sm5504.c
Normal file
File diff suppressed because it is too large
Load diff
1844
drivers/muic/tsu6721-muic.c
Normal file
1844
drivers/muic/tsu6721-muic.c
Normal file
File diff suppressed because it is too large
Load diff
159
drivers/muic/universal/muic-internal.h
Normal file
159
drivers/muic/universal/muic-internal.h
Normal file
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Samsung Electronics
|
||||
* Thomas Ryu <smilesr.ryu@samsung.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MUIC_INTERNAL_H__
|
||||
#define __MUIC_INTERNAL_H__
|
||||
|
||||
#include <linux/muic/muic.h>
|
||||
|
||||
#define MUIC_DEV_NAME "muic-universal"
|
||||
|
||||
/* Slave addr = 0x4A: MUIC */
|
||||
enum ioctl_cmd {
|
||||
GET_COM_VAL = 0x01,
|
||||
GET_CTLREG = 0x02,
|
||||
GET_ADC = 0x03,
|
||||
GET_SWITCHING_MODE = 0x04,
|
||||
GET_INT_MASK = 0x05,
|
||||
GET_REVISION = 0x06,
|
||||
GET_OTG_STATUS = 0x7,
|
||||
GET_CHGTYPE = 0x08,
|
||||
GET_RESID3 = 0x09,
|
||||
};
|
||||
|
||||
enum switching_mode{
|
||||
SWMODE_MANUAL =0,
|
||||
SWMODE_AUTO = 1,
|
||||
};
|
||||
|
||||
/*
|
||||
* Manual Switch
|
||||
* D- [7:5] / D+ [4:2] / Vbus [1:0]
|
||||
* 000: Open all / 001: USB / 010: AUDIO / 011: UART / 100: V_AUDIO
|
||||
* 00: Vbus to Open / 01: Vbus to Charger / 10: Vbus to MIC / 11: Vbus to VBout
|
||||
*/
|
||||
|
||||
/* COM port index */
|
||||
enum com_index {
|
||||
COM_OPEN = 1,
|
||||
COM_OPEN_WITH_V_BUS = 2,
|
||||
COM_UART_AP = 3,
|
||||
COM_UART_CP = 4,
|
||||
COM_USB_AP = 5,
|
||||
COM_USB_CP = 6,
|
||||
COM_AUDIO = 7,
|
||||
};
|
||||
|
||||
enum{
|
||||
ADC_SCANMODE_CONTINUOUS = 0x0,
|
||||
ADC_SCANMODE_ONESHOT = 0x1,
|
||||
ADC_SCANMODE_PULSE = 0x2,
|
||||
};
|
||||
|
||||
enum vps_type{
|
||||
VPS_TYPE_SCATTERED =0,
|
||||
VPS_TYPE_TABLE =1,
|
||||
};
|
||||
|
||||
/* VPS data from a chip. */
|
||||
typedef struct _muic_vps_scatterred_type {
|
||||
u8 val1;
|
||||
u8 val2;
|
||||
u8 val3;
|
||||
u8 adc;
|
||||
u8 vbvolt;
|
||||
}vps_scatterred_type;
|
||||
|
||||
typedef struct _muic_vps_table_t {
|
||||
u8 adc;
|
||||
u8 vbvolt;
|
||||
u8 adc1k;
|
||||
u8 adcerr;
|
||||
u8 adclow;
|
||||
u8 chgdetrun;
|
||||
u8 chgtyp;
|
||||
const char *vps_name;
|
||||
muic_attached_dev_t attached_dev;
|
||||
u8 control1;
|
||||
}vps_table_type;
|
||||
|
||||
struct muic_intr_data {
|
||||
u8 intr1;
|
||||
u8 intr2;
|
||||
u8 intr3;
|
||||
};
|
||||
|
||||
typedef union _muic_vps_t {
|
||||
vps_scatterred_type s;
|
||||
vps_table_type t;
|
||||
char vps_data[16];
|
||||
}vps_data_t;
|
||||
|
||||
|
||||
/* muic chip specific internal data structure
|
||||
* that setted at muic-xxxx.c file
|
||||
*/
|
||||
struct regmap_desc;
|
||||
|
||||
typedef struct _muic_data_t {
|
||||
|
||||
struct device *dev;
|
||||
struct i2c_client *i2c; /* i2c addr: 0x4A; MUIC */
|
||||
struct mutex muic_mutex;
|
||||
|
||||
/* model dependant muic platform data */
|
||||
struct muic_platform_data *pdata;
|
||||
|
||||
/* muic current attached device */
|
||||
muic_attached_dev_t attached_dev;
|
||||
|
||||
vps_data_t vps;
|
||||
int vps_table;
|
||||
|
||||
struct muic_intr_data intr;
|
||||
|
||||
/* regmap_desc_t */
|
||||
struct regmap_desc *regmapdesc;
|
||||
|
||||
char *chip_name;
|
||||
|
||||
int gpio_uart_sel;
|
||||
|
||||
/* muic Device ID */
|
||||
u8 muic_vendor; /* Vendor ID */
|
||||
u8 muic_version; /* Version ID */
|
||||
|
||||
bool is_usb_ready;
|
||||
bool is_factory_start;
|
||||
bool is_rustproof;
|
||||
bool is_otg_test;
|
||||
struct delayed_work init_work;
|
||||
struct delayed_work usb_work;
|
||||
|
||||
int is_flash_on;
|
||||
int irq_n;
|
||||
int is_afc_device;
|
||||
struct delayed_work afc_retry_work;
|
||||
struct delayed_work afc_restart_work;
|
||||
}muic_data_t;
|
||||
|
||||
extern struct device *switch_device;
|
||||
|
||||
#endif /* __MUIC_INTERNAL_H__ */
|
364
drivers/muic/universal/muic_afc.c
Normal file
364
drivers/muic/universal/muic_afc.c
Normal file
|
@ -0,0 +1,364 @@
|
|||
/*
|
||||
* muic_afc.c
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics
|
||||
* Jeongrae Kim <jryu.kim@samsung.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/muic/muic.h>
|
||||
#include <linux/muic/muic_afc.h>
|
||||
#include "muic-internal.h"
|
||||
#include "muic_regmap.h"
|
||||
#include "muic_i2c.h"
|
||||
#if defined(CONFIG_MUIC_NOTIFIER)
|
||||
#include <linux/muic/muic_notifier.h>
|
||||
#endif /* CONFIG_MUIC_NOTIFIER */
|
||||
|
||||
/* Bit 0 : VBUS_VAILD, Bit 1~7 : Reserved */
|
||||
#define REG_RSVDID1 0x15
|
||||
|
||||
#define REG_AFCTXD 0x19
|
||||
#define REG_VBUSSTAT 0x1b
|
||||
|
||||
muic_data_t *gpmuic;
|
||||
static int afc_work_state;
|
||||
|
||||
static int muic_is_afc_voltage(void);
|
||||
static int muic_dpreset_afc(void);
|
||||
static int muic_restart_afc(void);
|
||||
|
||||
/* To make AFC work properly on boot */
|
||||
static int is_charger_ready;
|
||||
static struct work_struct muic_afc_init_work;
|
||||
|
||||
int muic_check_afc_state(int state)
|
||||
{
|
||||
struct afc_ops *afcops = gpmuic->regmapdesc->afcops;
|
||||
int ret, retry;
|
||||
|
||||
pr_info("%s state = %d\n", __func__, state);
|
||||
|
||||
if (state) {
|
||||
/* Flash on state */
|
||||
if (muic_is_afc_voltage() && gpmuic->is_afc_device) {
|
||||
ret = muic_dpreset_afc();
|
||||
if (ret < 0) {
|
||||
pr_err("%s:failed to AFC reset(%d)\n",
|
||||
__func__, ret);
|
||||
}
|
||||
msleep(60); // 60ms delay
|
||||
|
||||
afcops->afc_ctrl_reg(gpmuic->regmapdesc, AFCCTRL_VBUS_READ, 1);
|
||||
afcops->afc_ctrl_reg(gpmuic->regmapdesc, AFCCTRL_VBUS_READ, 0);
|
||||
for (retry = 0; retry <20; retry++) {
|
||||
mdelay(20);
|
||||
ret = muic_is_afc_voltage();
|
||||
if (!ret) {
|
||||
pr_info("%s:AFC Reset Success(%d)\n",
|
||||
__func__, ret);
|
||||
gpmuic->is_flash_on = 1;
|
||||
return 1;
|
||||
} else {
|
||||
pr_info("%s:AFC Reset Failed(%d)\n",
|
||||
__func__, ret);
|
||||
gpmuic->is_flash_on = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pr_info("%s:Not connected AFC\n",__func__);
|
||||
gpmuic->is_flash_on = 1;
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
/* Flash off state */
|
||||
if ((gpmuic->attached_dev == ATTACHED_DEV_AFC_CHARGER_5V_MUIC) ||
|
||||
((gpmuic->is_afc_device) && (gpmuic->attached_dev != ATTACHED_DEV_AFC_CHARGER_9V_MUIC)))
|
||||
muic_restart_afc();
|
||||
gpmuic->is_flash_on = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(muic_check_afc_state);
|
||||
|
||||
int muic_torch_prepare(int state)
|
||||
{
|
||||
struct afc_ops *afcops = gpmuic->regmapdesc->afcops;
|
||||
int ret, retry;
|
||||
|
||||
pr_info("%s state = %d\n", __func__, state);
|
||||
|
||||
if (afc_work_state == 1) {
|
||||
pr_info("%s:%s cancel_delayed_work afc_work_state=%d\n",MUIC_DEV_NAME, __func__, afc_work_state);
|
||||
cancel_delayed_work(&gpmuic->afc_restart_work);
|
||||
afc_work_state = 0;
|
||||
}
|
||||
|
||||
if (state) {
|
||||
/* Torch on state */
|
||||
if (muic_is_afc_voltage() && gpmuic->is_afc_device) {
|
||||
ret = muic_dpreset_afc();
|
||||
msleep(60); // 60ms delay
|
||||
if (ret < 0) {
|
||||
pr_err("%s:failed to AFC reset(%d)\n",
|
||||
__func__, ret);
|
||||
}
|
||||
afcops->afc_ctrl_reg(gpmuic->regmapdesc, AFCCTRL_VBUS_READ, 1);
|
||||
afcops->afc_ctrl_reg(gpmuic->regmapdesc, AFCCTRL_VBUS_READ, 0);
|
||||
for (retry = 0; retry <20; retry++) {
|
||||
mdelay(20);
|
||||
ret = muic_is_afc_voltage();
|
||||
if (!ret) {
|
||||
pr_info("%s:AFC Reset Success(%d)\n",
|
||||
__func__, ret);
|
||||
gpmuic->is_flash_on = 1;
|
||||
return 1;
|
||||
} else {
|
||||
pr_info("%s:AFC Reset Failed(%d)\n",
|
||||
__func__, ret);
|
||||
gpmuic->is_flash_on = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pr_info("%s:Not connected AFC\n",__func__);
|
||||
gpmuic->is_flash_on = 1;
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
/* Torch off state */
|
||||
gpmuic->is_flash_on = 0;
|
||||
if ((gpmuic->attached_dev == ATTACHED_DEV_AFC_CHARGER_5V_MUIC) ||
|
||||
((gpmuic->is_afc_device) && (gpmuic->attached_dev != ATTACHED_DEV_AFC_CHARGER_9V_MUIC))) {
|
||||
schedule_delayed_work(&gpmuic->afc_restart_work, msecs_to_jiffies(5000)); // 20sec
|
||||
pr_info("%s:%s AFC_torch_work start \n",MUIC_DEV_NAME, __func__ );
|
||||
afc_work_state = 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(muic_torch_prepare);
|
||||
|
||||
static int muic_is_afc_voltage(void)
|
||||
{
|
||||
struct i2c_client *i2c = gpmuic->i2c;
|
||||
int vbus_status;
|
||||
|
||||
if (gpmuic->attached_dev == ATTACHED_DEV_NONE_MUIC) {
|
||||
pr_info("%s attached_dev None \n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
vbus_status = muic_i2c_read_byte(i2c, REG_VBUSSTAT);
|
||||
vbus_status = (vbus_status & 0x0F);
|
||||
pr_info("%s vbus_status (%d)\n", __func__, vbus_status);
|
||||
if (vbus_status == 0x00)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
int muic_dpreset_afc(void)
|
||||
{
|
||||
struct afc_ops *afcops = gpmuic->regmapdesc->afcops;
|
||||
|
||||
pr_info("%s: gpmuic->attached_dev = %d\n", __func__, gpmuic->attached_dev);
|
||||
if ( (gpmuic->attached_dev == ATTACHED_DEV_AFC_CHARGER_9V_MUIC) ||
|
||||
(gpmuic->attached_dev == ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC) ||
|
||||
(muic_is_afc_voltage()) ) {
|
||||
// ENAFC set '0'
|
||||
afcops->afc_ctrl_reg(gpmuic->regmapdesc, AFCCTRL_ENAFC, 0);
|
||||
msleep(50); // 50ms delay
|
||||
|
||||
// DP_RESET
|
||||
pr_info("%s:AFC Disable \n", __func__);
|
||||
afcops->afc_ctrl_reg(gpmuic->regmapdesc, AFCCTRL_DIS_AFC, 1);
|
||||
msleep(20);
|
||||
afcops->afc_ctrl_reg(gpmuic->regmapdesc, AFCCTRL_DIS_AFC, 0);
|
||||
|
||||
gpmuic->attached_dev = ATTACHED_DEV_AFC_CHARGER_5V_MUIC;
|
||||
muic_notifier_attach_attached_dev(ATTACHED_DEV_AFC_CHARGER_5V_MUIC);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int muic_restart_afc(void)
|
||||
{
|
||||
struct i2c_client *i2c = gpmuic->i2c;
|
||||
int ret, value;
|
||||
struct afc_ops *afcops = gpmuic->regmapdesc->afcops;
|
||||
|
||||
pr_info("%s:AFC Restart attached_dev = 0x%x\n", __func__, gpmuic->attached_dev);
|
||||
msleep(120); // 120ms delay
|
||||
if (gpmuic->attached_dev == ATTACHED_DEV_NONE_MUIC) {
|
||||
pr_info("%s:%s Device type is None\n",MUIC_DEV_NAME, __func__);
|
||||
return 0;
|
||||
}
|
||||
gpmuic->attached_dev = ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC;
|
||||
muic_notifier_attach_attached_dev(ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC);
|
||||
cancel_delayed_work(&gpmuic->afc_retry_work);
|
||||
schedule_delayed_work(&gpmuic->afc_retry_work, msecs_to_jiffies(5000)); // 5sec
|
||||
|
||||
// voltage(9.0V) + current(1.65A) setting : 0x
|
||||
value = 0x46;
|
||||
ret = muic_i2c_write_byte(i2c, REG_AFCTXD, value);
|
||||
if (ret < 0)
|
||||
printk(KERN_ERR "[muic] %s: err write AFC_TXD(%d)\n", __func__, ret);
|
||||
pr_info("%s:AFC_TXD [0x%02x]\n", __func__, value);
|
||||
|
||||
// ENAFC set '1'
|
||||
afcops->afc_ctrl_reg(gpmuic->regmapdesc, AFCCTRL_ENAFC, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void muic_afc_restart_work(struct work_struct *work)
|
||||
{
|
||||
struct i2c_client *i2c = gpmuic->i2c;
|
||||
int ret, value;
|
||||
struct afc_ops *afcops = gpmuic->regmapdesc->afcops;
|
||||
|
||||
pr_info("%s:AFC Restart\n", __func__);
|
||||
msleep(120); // 120ms delay
|
||||
if (gpmuic->attached_dev == ATTACHED_DEV_NONE_MUIC) {
|
||||
pr_info("%s:%s Device type is None\n",MUIC_DEV_NAME, __func__);
|
||||
return;
|
||||
}
|
||||
gpmuic->attached_dev = ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC;
|
||||
muic_notifier_attach_attached_dev(ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC);
|
||||
cancel_delayed_work(&gpmuic->afc_retry_work);
|
||||
schedule_delayed_work(&gpmuic->afc_retry_work, msecs_to_jiffies(5000)); // 5sec
|
||||
|
||||
// voltage(9.0V) + current(1.65A) setting : 0x
|
||||
value = 0x46;
|
||||
ret = muic_i2c_write_byte(i2c, REG_AFCTXD, value);
|
||||
if (ret < 0)
|
||||
printk(KERN_ERR "[muic] %s: err write AFC_TXD(%d)\n", __func__, ret);
|
||||
pr_info("%s:AFC_TXD [0x%02x]\n", __func__, value);
|
||||
|
||||
// ENAFC set '1'
|
||||
afcops->afc_ctrl_reg(gpmuic->regmapdesc, AFCCTRL_ENAFC, 1);
|
||||
afc_work_state = 0;
|
||||
}
|
||||
|
||||
static void muic_afc_retry_work(struct work_struct *work)
|
||||
{
|
||||
struct i2c_client *i2c = gpmuic->i2c;
|
||||
struct afc_ops *afcops = gpmuic->regmapdesc->afcops;
|
||||
int vbus;
|
||||
|
||||
pr_info("%s:AFC retry work\n", __func__);
|
||||
if (gpmuic->attached_dev == ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC) {
|
||||
vbus = muic_i2c_read_byte(i2c, REG_RSVDID1);
|
||||
if (!(vbus & 0x01)) {
|
||||
pr_info("%s:%s VBUS is nothing\n",MUIC_DEV_NAME, __func__);
|
||||
gpmuic->attached_dev = ATTACHED_DEV_NONE_MUIC;
|
||||
muic_notifier_attach_attached_dev(ATTACHED_DEV_NONE_MUIC);
|
||||
return;
|
||||
}
|
||||
|
||||
pr_info("%s: [MUIC] devtype is afc prepare - Disable AFC\n", __func__);
|
||||
afcops->afc_ctrl_reg(gpmuic->regmapdesc, AFCCTRL_DIS_AFC, 1);
|
||||
msleep(20);
|
||||
afcops->afc_ctrl_reg(gpmuic->regmapdesc, AFCCTRL_DIS_AFC, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void muic_focrced_detection_by_charger(struct work_struct *work)
|
||||
{
|
||||
struct afc_ops *afcops = gpmuic->regmapdesc->afcops;
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
mutex_lock(&gpmuic->muic_mutex);
|
||||
|
||||
afcops->afc_init_check(gpmuic->regmapdesc);
|
||||
|
||||
mutex_unlock(&gpmuic->muic_mutex);
|
||||
}
|
||||
|
||||
void muic_charger_init(void)
|
||||
{
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
if (!gpmuic) {
|
||||
pr_info("%s: MUIC AFC is not ready.\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_charger_ready) {
|
||||
pr_info("%s: charger is already ready.\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
is_charger_ready = true;
|
||||
|
||||
if (gpmuic->attached_dev == ATTACHED_DEV_TA_MUIC)
|
||||
schedule_work(&muic_afc_init_work);
|
||||
}
|
||||
|
||||
static ssize_t afc_off_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
muic_data_t *pmuic = dev_get_drvdata(dev);
|
||||
return snprintf(buf, 4, "%d\n", pmuic->is_flash_on);
|
||||
}
|
||||
|
||||
static ssize_t afc_off_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
if (!strncmp(buf, "1", 1)) {
|
||||
pr_info("%s, Disable AFC\n", __func__);
|
||||
muic_check_afc_state(1);
|
||||
} else {
|
||||
pr_info("%s, Enable AFC\n", __func__);
|
||||
muic_check_afc_state(0);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR(afc_off, S_IRUGO | S_IWUSR,
|
||||
afc_off_show, afc_off_store);
|
||||
void muic_init_afc_state(muic_data_t *pmuic)
|
||||
{
|
||||
int ret;
|
||||
gpmuic = pmuic;
|
||||
gpmuic->is_flash_on = 0;
|
||||
gpmuic->is_afc_device = 0;
|
||||
INIT_DELAYED_WORK(&gpmuic->afc_restart_work, muic_afc_restart_work);
|
||||
|
||||
/* To make AFC work properly on boot */
|
||||
INIT_WORK(&muic_afc_init_work, muic_focrced_detection_by_charger);
|
||||
INIT_DELAYED_WORK(&gpmuic->afc_retry_work, muic_afc_retry_work);
|
||||
|
||||
ret = device_create_file(switch_device, &dev_attr_afc_off);
|
||||
if (ret < 0) {
|
||||
pr_err("[MUIC] Failed to create file (disable AFC)!\n");
|
||||
}
|
||||
|
||||
pr_info("%s:attached_dev = %d\n", __func__, gpmuic->attached_dev);
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("MUIC driver");
|
||||
MODULE_AUTHOR("<jryu.kim@samsung.com>");
|
||||
MODULE_LICENSE("GPL");
|
831
drivers/muic/universal/muic_apis.c
Normal file
831
drivers/muic/universal/muic_apis.c
Normal file
|
@ -0,0 +1,831 @@
|
|||
/*
|
||||
* muic_apis.c
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics
|
||||
* Thomas Ryu <smilesr.ryu@samsung.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/host_notify.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <linux/muic/muic.h>
|
||||
|
||||
#if defined(CONFIG_MUIC_NOTIFIER)
|
||||
#include <linux/muic/muic_notifier.h>
|
||||
#endif /* CONFIG_MUIC_NOTIFIER */
|
||||
|
||||
#if defined (CONFIG_OF)
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
#include "muic-internal.h"
|
||||
#include "muic_i2c.h"
|
||||
#include "muic_regmap.h"
|
||||
|
||||
int attach_ta(muic_data_t *pmuic)
|
||||
{
|
||||
struct vendor_ops *pvendor = pmuic->regmapdesc->vendorops;
|
||||
|
||||
if (pvendor->attach_ta) {
|
||||
pr_info("%s: ", __func__);
|
||||
pvendor->attach_ta(pmuic->regmapdesc);
|
||||
} else
|
||||
pr_info("%s: No Vendor API ready.\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int detach_ta(muic_data_t *pmuic)
|
||||
{
|
||||
struct vendor_ops *pvendor = pmuic->regmapdesc->vendorops;
|
||||
|
||||
if (pvendor->detach_ta) {
|
||||
pr_info("%s: ", __func__);
|
||||
pvendor->detach_ta(pmuic->regmapdesc);
|
||||
} else
|
||||
pr_info("%s: No Vendor API ready.\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_charger_type(muic_data_t *pmuic)
|
||||
{
|
||||
struct regmap_ops *pops = pmuic->regmapdesc->regmapops;
|
||||
int uattr;
|
||||
|
||||
pops->ioctl(pmuic->regmapdesc, GET_CHGTYPE, NULL, &uattr);
|
||||
return regmap_read_value(pmuic->regmapdesc, uattr);
|
||||
}
|
||||
|
||||
static int set_BCD_RESCAN_reg(muic_data_t *pmuic, int value)
|
||||
{
|
||||
struct regmap_ops *pops = pmuic->regmapdesc->regmapops;
|
||||
struct vendor_ops *pvendor = pmuic->regmapdesc->vendorops;
|
||||
int uattr, ret;
|
||||
|
||||
if (pvendor && pvendor->rescan) {
|
||||
ret = pvendor->rescan(pmuic->regmapdesc, value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pops->ioctl(pmuic->regmapdesc, GET_RESID3, NULL, &uattr);
|
||||
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_SM5504)
|
||||
value = !value;
|
||||
#else
|
||||
uattr |= _ATTR_OVERWRITE_M;
|
||||
#endif
|
||||
|
||||
ret = regmap_write_value(pmuic->regmapdesc, uattr, value);
|
||||
_REGMAP_TRACE(pmuic->regmapdesc, 'w', ret, uattr, value);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int do_BCD_rescan(muic_data_t *pmuic)
|
||||
{
|
||||
static int bcd_rescan = 0;
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
if (bcd_rescan) {
|
||||
int chg_type = get_charger_type(pmuic);
|
||||
int new_dev = 0;
|
||||
|
||||
bcd_rescan = 0;
|
||||
|
||||
if (chg_type < 0)
|
||||
pr_err("%s:%s err %d\n", MUIC_DEV_NAME, __func__, chg_type);
|
||||
|
||||
pr_info("%s [MUIC] BCD result chg_type = 0x%x \n", __func__, chg_type);
|
||||
switch(chg_type) {
|
||||
case 0x01 : // DCP
|
||||
new_dev = ATTACHED_DEV_TA_MUIC;
|
||||
break;
|
||||
case 0x02 : // CDP
|
||||
new_dev = ATTACHED_DEV_CDP_MUIC;
|
||||
break;
|
||||
case 0x04 : // SDP
|
||||
new_dev = ATTACHED_DEV_USB_MUIC;
|
||||
break;
|
||||
case 0x08 : // Time out SDP
|
||||
new_dev = ATTACHED_DEV_USB_MUIC;
|
||||
break;
|
||||
case 0x10 : // U200
|
||||
new_dev = ATTACHED_DEV_TA_MUIC;
|
||||
break;
|
||||
}
|
||||
|
||||
return new_dev;
|
||||
}
|
||||
|
||||
pr_info("[MUIC] 219K USB Cable/Charger Connected\n");
|
||||
pr_info("[MUIC] BCD rescan\n");
|
||||
|
||||
// 0x21 -> 1 0x21 -> 0
|
||||
set_BCD_RESCAN_reg(pmuic, 0x01);
|
||||
msleep(1);
|
||||
pr_info("[MUIC] Writing BCD_RECAN\n");
|
||||
set_BCD_RESCAN_reg(pmuic, 0x00);
|
||||
|
||||
bcd_rescan = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_switch_mode(muic_data_t *pmuic)
|
||||
{
|
||||
struct vendor_ops *pvendor = pmuic->regmapdesc->vendorops;
|
||||
int val=0;
|
||||
|
||||
if (pvendor->get_switch) {
|
||||
pr_info("%s: ", __func__);
|
||||
val = pvendor->get_switch(pmuic->regmapdesc);
|
||||
} else{
|
||||
pr_info("%s: No Vendor API ready.\n", __func__);
|
||||
val = -1;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
void set_switch_mode(muic_data_t *pmuic, int mode)
|
||||
{
|
||||
struct vendor_ops *pvendor = pmuic->regmapdesc->vendorops;
|
||||
|
||||
if (pvendor->set_switch) {
|
||||
pr_info("%s: ", __func__);
|
||||
pvendor->set_switch(pmuic->regmapdesc,mode);
|
||||
} else{
|
||||
pr_info("%s: No Vendor API ready.\n", __func__);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int get_adc_scan_mode(muic_data_t *pmuic)
|
||||
{
|
||||
struct vendor_ops *pvendor = pmuic->regmapdesc->vendorops;
|
||||
int value = 0;
|
||||
|
||||
if (pvendor->get_adc_scan_mode) {
|
||||
pr_info("%s: ", __func__);
|
||||
value = pvendor->get_adc_scan_mode(pmuic->regmapdesc);
|
||||
} else{
|
||||
pr_info("%s: No Vendor API ready.\n", __func__);
|
||||
value = -1;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void set_adc_scan_mode(muic_data_t *pmuic, const u8 val)
|
||||
{
|
||||
struct vendor_ops *pvendor = pmuic->regmapdesc->vendorops;
|
||||
|
||||
if (pvendor->set_adc_scan_mode) {
|
||||
pr_info("%s: ", __func__);
|
||||
pvendor->set_adc_scan_mode(pmuic->regmapdesc,val);
|
||||
} else
|
||||
pr_info("%s: No Vendor API ready.\n", __func__);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#define com_to_open com_to_open_with_vbus
|
||||
|
||||
#ifndef com_to_open
|
||||
int com_to_open(muic_data_t *pmuic)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = regmap_com_to(pmuic->regmapdesc, COM_OPEN);
|
||||
if (ret < 0)
|
||||
pr_err("%s:%s com_to_open err\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
int com_to_open_with_vbus(muic_data_t *pmuic)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
ret = regmap_com_to(pmuic->regmapdesc, COM_OPEN_WITH_V_BUS);
|
||||
if (ret < 0)
|
||||
pr_err("%s:%s com_to_open err\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int com_to_usb_ap(muic_data_t *pmuic)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
ret = regmap_com_to(pmuic->regmapdesc, COM_USB_AP);
|
||||
if (ret < 0)
|
||||
pr_err("%s:%s set_com_usb err\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int com_to_usb_cp(muic_data_t *pmuic)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
ret = regmap_com_to(pmuic->regmapdesc, COM_USB_CP);
|
||||
if (ret < 0)
|
||||
pr_err("%s:%s set_com_usb err\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_rustproof_mode(struct regmap_desc *pdesc, int op)
|
||||
{
|
||||
struct vendor_ops *pvendor = pdesc->vendorops;
|
||||
|
||||
if (pvendor->set_rustproof) {
|
||||
pr_info("%s: %s", __func__, op ? "On" : "Off");
|
||||
pvendor->set_rustproof(pdesc, op);
|
||||
} else
|
||||
pr_err("%s: No Vendor API ready.\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int com_to_uart_ap(muic_data_t *pmuic)
|
||||
{
|
||||
int com_index = 0, ret = 0;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
com_index = pmuic->is_rustproof ? COM_OPEN_WITH_V_BUS : COM_UART_AP;
|
||||
|
||||
ret = regmap_com_to(pmuic->regmapdesc, com_index);
|
||||
if (ret < 0)
|
||||
pr_err("%s:%s set_com_uart err\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
if(pmuic->is_rustproof) {
|
||||
pr_info("%s:%s rustproof mode Enabled\n",
|
||||
MUIC_DEV_NAME, __func__);
|
||||
|
||||
set_rustproof_mode(pmuic->regmapdesc, 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int com_to_uart_cp(muic_data_t *pmuic)
|
||||
{
|
||||
int com_index = 0, ret = 0;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
com_index = pmuic->is_rustproof ? COM_OPEN_WITH_V_BUS : COM_UART_CP;
|
||||
|
||||
ret = regmap_com_to(pmuic->regmapdesc, com_index);
|
||||
if (ret < 0)
|
||||
pr_err("%s:%s set_com_uart err\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
if(pmuic->is_rustproof) {
|
||||
pr_info("%s:%s rustproof mode Enabled\n",
|
||||
MUIC_DEV_NAME, __func__);
|
||||
|
||||
set_rustproof_mode(pmuic->regmapdesc, 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int com_to_audio(muic_data_t *pmuic)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = regmap_com_to(pmuic->regmapdesc, COM_AUDIO);
|
||||
if (ret < 0)
|
||||
pr_err("%s:%s set_com_audio err\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int switch_to_ap_usb(muic_data_t *pmuic)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
ret = com_to_usb_ap(pmuic);
|
||||
if (ret < 0) {
|
||||
pr_err("%s:%s com->usb set err\n", MUIC_DEV_NAME, __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int switch_to_cp_usb(muic_data_t *pmuic)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
ret = com_to_usb_cp(pmuic);
|
||||
if (ret < 0) {
|
||||
pr_err("%s:%s com->usb set err\n", MUIC_DEV_NAME, __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int switch_to_ap_uart(muic_data_t *pmuic)
|
||||
{
|
||||
struct muic_platform_data *pdata = pmuic->pdata;
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
if (pdata->set_gpio_uart_sel)
|
||||
pdata->set_gpio_uart_sel(MUIC_PATH_UART_AP);
|
||||
|
||||
ret = com_to_uart_ap(pmuic);
|
||||
if (ret < 0) {
|
||||
pr_err("%s:%s com->uart set err\n", MUIC_DEV_NAME, __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int switch_to_cp_uart(muic_data_t *pmuic)
|
||||
{
|
||||
struct muic_platform_data *pdata = pmuic->pdata;
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
if (pdata->set_gpio_uart_sel)
|
||||
pdata->set_gpio_uart_sel(MUIC_PATH_UART_CP);
|
||||
|
||||
ret = com_to_uart_cp(pmuic);
|
||||
if (ret < 0) {
|
||||
pr_err("%s:%s com->uart set err\n", MUIC_DEV_NAME, __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int attach_uart_util(muic_data_t *pmuic,
|
||||
muic_attached_dev_t new_dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pmuic->attached_dev = new_dev;
|
||||
if (pmuic->pdata->usb_path == MUIC_PATH_UART_AP) {
|
||||
ret = switch_to_ap_uart(pmuic);
|
||||
}
|
||||
else if (pmuic->pdata->usb_path == MUIC_PATH_UART_CP) {
|
||||
ret = switch_to_cp_uart(pmuic);
|
||||
}
|
||||
else
|
||||
pr_warn("%s:%s invalid usb_path\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int attach_usb_util(muic_data_t *pmuic,
|
||||
muic_attached_dev_t new_dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pmuic->attached_dev = new_dev;
|
||||
if (pmuic->pdata->usb_path == MUIC_PATH_USB_AP) {
|
||||
ret = switch_to_ap_usb(pmuic);
|
||||
}
|
||||
else if (pmuic->pdata->usb_path == MUIC_PATH_USB_CP) {
|
||||
ret = switch_to_cp_usb(pmuic);
|
||||
}
|
||||
else
|
||||
pr_warn("%s:%s invalid usb_path\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int attach_usb(muic_data_t *pmuic,
|
||||
muic_attached_dev_t new_dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (pmuic->attached_dev == new_dev) {
|
||||
pr_info("%s:%s duplicated(USB)\n", MUIC_DEV_NAME, __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
ret = attach_usb_util(pmuic, new_dev);
|
||||
if (ret < 0) {
|
||||
pr_err("%s:%s fail.(%d)\n", MUIC_DEV_NAME, __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int detach_usb(muic_data_t *pmuic)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s attached_dev type(%d)\n", MUIC_DEV_NAME, __func__,
|
||||
pmuic->attached_dev);
|
||||
|
||||
ret = com_to_open_with_vbus(pmuic);
|
||||
if (ret < 0) {
|
||||
pr_err("%s:%s fail.(%d)\n", MUIC_DEV_NAME, __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
pmuic->attached_dev = ATTACHED_DEV_NONE_MUIC;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int attach_otg_usb(muic_data_t *pmuic,
|
||||
muic_attached_dev_t new_dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (pmuic->attached_dev == new_dev) {
|
||||
pr_info("%s:%s duplicated(USB)\n", MUIC_DEV_NAME, __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
/* LANHUB doesn't work under AUTO switch mode, so turn it off */
|
||||
/* set MANUAL SW mode */
|
||||
set_switch_mode(pmuic,SWMODE_MANUAL);
|
||||
|
||||
/* enable RAW DATA mode, only for OTG LANHUB */
|
||||
set_adc_scan_mode(pmuic,ADC_SCANMODE_CONTINUOUS);
|
||||
|
||||
ret = switch_to_ap_usb(pmuic);
|
||||
|
||||
pmuic->attached_dev = new_dev;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int detach_otg_usb(muic_data_t *pmuic)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s attached_dev type(%d)\n", MUIC_DEV_NAME, __func__,
|
||||
pmuic->attached_dev);
|
||||
|
||||
ret = com_to_open_with_vbus(pmuic);
|
||||
if (ret < 0) {
|
||||
pr_err("%s:%s fail.(%d)\n", MUIC_DEV_NAME, __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* disable RAW DATA mode */
|
||||
set_adc_scan_mode(pmuic,ADC_SCANMODE_ONESHOT);
|
||||
|
||||
/* set AUTO SW mode */
|
||||
set_switch_mode(pmuic,SWMODE_AUTO);
|
||||
|
||||
pmuic->attached_dev = ATTACHED_DEV_NONE_MUIC;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int attach_ps_cable(muic_data_t *pmuic,
|
||||
muic_attached_dev_t new_dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s new_dev(%d)\n", MUIC_DEV_NAME, __func__, new_dev);
|
||||
com_to_open_with_vbus(pmuic);
|
||||
|
||||
pmuic->attached_dev = new_dev;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int detach_ps_cable(muic_data_t *pmuic)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
pmuic->attached_dev = ATTACHED_DEV_NONE_MUIC;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int attach_deskdock(muic_data_t *pmuic,
|
||||
muic_attached_dev_t new_dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
/* Audio-out doesn't work under AUTO switch mode, so turn it off */
|
||||
/* set MANUAL SW mode */
|
||||
set_switch_mode(pmuic,SWMODE_MANUAL);
|
||||
ret = com_to_audio(pmuic);
|
||||
if (ret < 0) {
|
||||
pr_err("%s:%s fail.(%d)\n", MUIC_DEV_NAME, __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pmuic->attached_dev = new_dev;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int detach_deskdock(muic_data_t *pmuic)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
set_switch_mode(pmuic,SWMODE_AUTO);
|
||||
pmuic->attached_dev = ATTACHED_DEV_NONE_MUIC;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int attach_audiodock(muic_data_t *pmuic,
|
||||
muic_attached_dev_t new_dev, u8 vbus)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
ret = attach_usb_util(pmuic, new_dev);
|
||||
if (ret < 0)
|
||||
pr_err("%s:%s fail.(%d)\n", MUIC_DEV_NAME, __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int detach_audiodock(muic_data_t *pmuic)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
ret = com_to_open_with_vbus(pmuic);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pmuic->attached_dev = ATTACHED_DEV_NONE_MUIC;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int attach_jig_uart_boot_off(muic_data_t *pmuic, muic_attached_dev_t new_dev,
|
||||
u8 vbvolt)
|
||||
{
|
||||
struct muic_platform_data *pdata = pmuic->pdata;
|
||||
struct vendor_ops *pvendor = pmuic->regmapdesc->vendorops;
|
||||
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s JIG UART BOOT-OFF(0x%x)\n", MUIC_DEV_NAME, __func__,
|
||||
vbvolt);
|
||||
|
||||
if (pdata->uart_path == MUIC_PATH_UART_AP)
|
||||
ret = switch_to_ap_uart(pmuic);
|
||||
else
|
||||
ret = switch_to_cp_uart(pmuic);
|
||||
|
||||
if (pvendor && pvendor->set_manual_JIGON)
|
||||
pvendor->set_manual_JIGON(pmuic->regmapdesc, 1);
|
||||
|
||||
/* if VBUS is enabled, call host_notify_cb to check if it is OTGTEST*/
|
||||
if (vbvolt) {
|
||||
if (pmuic->is_otg_test) {
|
||||
pr_info("%s:%s OTG_TEST\n", MUIC_DEV_NAME, __func__);
|
||||
/* in OTG_TEST mode, do not charge */
|
||||
// new_dev = ATTACHED_DEV_JIG_UART_OFF_VB_OTG_MUIC;
|
||||
new_dev = ATTACHED_DEV_JIG_UART_OFF_MUIC;
|
||||
} else
|
||||
/* JIG_UART_OFF_VB */
|
||||
new_dev = ATTACHED_DEV_JIG_UART_OFF_VB_MUIC;
|
||||
} else
|
||||
new_dev = ATTACHED_DEV_JIG_UART_OFF_MUIC;
|
||||
|
||||
pmuic->attached_dev = new_dev;
|
||||
|
||||
return new_dev;
|
||||
}
|
||||
int detach_jig_uart_boot_off(muic_data_t *pmuic)
|
||||
{
|
||||
struct vendor_ops *pvendor = pmuic->regmapdesc->vendorops;
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
if (pvendor && pvendor->set_manual_JIGON)
|
||||
pvendor->set_manual_JIGON(pmuic->regmapdesc, 0);
|
||||
|
||||
if(pmuic->is_rustproof) {
|
||||
pr_info("%s:%s rustproof mode : Set Auto SW mode\n",
|
||||
MUIC_DEV_NAME, __func__);
|
||||
|
||||
set_rustproof_mode(pmuic->regmapdesc, 0);
|
||||
}
|
||||
|
||||
pmuic->attached_dev = ATTACHED_DEV_NONE_MUIC;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* QA's requirement on JIG_UART_ON(619K)
|
||||
* 1. Factory Test Mode
|
||||
* Send a deskdock Noti. to wakeup the device.
|
||||
* (OPEN->619K->OPEN->523K )
|
||||
* 2. Normal
|
||||
* Do not charge the device. (No charging Icon and current)
|
||||
* Need to set the path OPEN and cut off VBUS input.
|
||||
*/
|
||||
int attach_jig_uart_boot_on(muic_data_t *pmuic, muic_attached_dev_t new_dev)
|
||||
{
|
||||
int com_index = COM_OPEN;
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s JIG UART BOOT-ON(0x%x)\n",
|
||||
MUIC_DEV_NAME, __func__, new_dev);
|
||||
|
||||
ret = regmap_com_to(pmuic->regmapdesc, com_index);
|
||||
if (ret < 0)
|
||||
pr_err("%s:%s set_com_uart err\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
|
||||
pr_info("%s:%s rustproof mode is set.\n",
|
||||
MUIC_DEV_NAME, __func__);
|
||||
|
||||
set_rustproof_mode(pmuic->regmapdesc, 1);
|
||||
|
||||
pmuic->attached_dev = new_dev;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int detach_jig_uart_boot_on(muic_data_t *pmuic)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s rustproof mode is restored.\n",
|
||||
MUIC_DEV_NAME, __func__);
|
||||
|
||||
set_rustproof_mode(pmuic->regmapdesc, 0);
|
||||
|
||||
pmuic->attached_dev = ATTACHED_DEV_NONE_MUIC;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int attach_jig_usb_boot_off(muic_data_t *pmuic,
|
||||
u8 vbvolt)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (pmuic->attached_dev == ATTACHED_DEV_JIG_USB_OFF_MUIC) {
|
||||
pr_info("%s:%s duplicated(JIG USB OFF)\n", MUIC_DEV_NAME,
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
ret = attach_usb_util(pmuic, ATTACHED_DEV_JIG_USB_OFF_MUIC);
|
||||
if (ret < 0) {
|
||||
pr_err("%s:%s fail.(%d)\n", MUIC_DEV_NAME, __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int attach_jig_usb_boot_on(muic_data_t *pmuic,
|
||||
u8 vbvolt)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (pmuic->attached_dev == ATTACHED_DEV_JIG_USB_ON_MUIC) {
|
||||
pr_info("%s:%s duplicated(JIG USB ON)\n", MUIC_DEV_NAME,
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
ret = attach_usb_util(pmuic, ATTACHED_DEV_JIG_USB_ON_MUIC);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int attach_mhl(muic_data_t *pmuic)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
ret = com_to_open_with_vbus(pmuic);
|
||||
if (ret < 0) {
|
||||
pr_err("%s:%s fail.(%d)\n", MUIC_DEV_NAME, __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pmuic->attached_dev = ATTACHED_DEV_MHL_MUIC;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int detach_mhl(muic_data_t *pmuic)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
if(ret < 0)
|
||||
pr_err("%s:%s err detach_charger(%d)\n", MUIC_DEV_NAME, __func__, ret);
|
||||
|
||||
pmuic->attached_dev = ATTACHED_DEV_NONE_MUIC;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int get_adc(muic_data_t *pmuic)
|
||||
{
|
||||
struct regmap_ops *pops = pmuic->regmapdesc->regmapops;
|
||||
int uattr;
|
||||
|
||||
pops->ioctl(pmuic->regmapdesc, GET_ADC, NULL, &uattr);
|
||||
return regmap_read_value(pmuic->regmapdesc, uattr);
|
||||
}
|
||||
|
||||
int get_vps_data(muic_data_t *pmuic, void *pdata)
|
||||
{
|
||||
struct vendor_ops *pvendor = pmuic->regmapdesc->vendorops;
|
||||
|
||||
if (pvendor->get_vps_data)
|
||||
pvendor->get_vps_data(pmuic->regmapdesc, pdata);
|
||||
else
|
||||
pr_info("%s: No Vendor API ready.\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_UART_SEL
|
||||
int java_muic_set_path(void *drv_data, int path)
|
||||
{
|
||||
muic_data_t *pmuic = (muic_data_t *)drv_data;
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s: %s path : %d\n", MUIC_DEV_NAME, __func__, path);
|
||||
|
||||
if(!pmuic->is_rustproof) {
|
||||
if (path) { /* UART */
|
||||
ret = regmap_com_to(pmuic->regmapdesc, COM_UART_AP);
|
||||
if (ret < 0)
|
||||
pr_err("%s:%s set_com_uart err\n", MUIC_DEV_NAME, __func__);
|
||||
set_rustproof_mode(pmuic->regmapdesc, 1);
|
||||
} else { /* USB */
|
||||
ret = regmap_com_to(pmuic->regmapdesc, COM_USB_AP);
|
||||
if (ret < 0)
|
||||
pr_err("%s:%s set_com_usb err\n", MUIC_DEV_NAME, __func__);
|
||||
set_rustproof_mode(pmuic->regmapdesc, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
56
drivers/muic/universal/muic_apis.h
Normal file
56
drivers/muic/universal/muic_apis.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
#ifndef _MUIC_APIS_
|
||||
#define _MUIC_APIS_
|
||||
|
||||
extern int attach_ta(muic_data_t *pmuic);
|
||||
extern int detach_ta(muic_data_t *pmuic);
|
||||
extern int do_BCD_rescan(muic_data_t *pmuic);
|
||||
extern int enable_periodic_adc_scan(muic_data_t *pmuic);
|
||||
extern int disable_periodic_adc_scan(muic_data_t *pmuic);
|
||||
extern int com_to_open_with_vbus(muic_data_t *pmuic);
|
||||
extern int com_to_usb(muic_data_t *pmuic);
|
||||
extern int com_to_uart(muic_data_t *pmuic);
|
||||
extern int com_to_audio(muic_data_t *pmuic);
|
||||
extern int switch_to_ap_usb(muic_data_t *pmuic);
|
||||
extern int switch_to_ap_uart(muic_data_t *pmuic);
|
||||
extern int switch_to_cp_uart(muic_data_t *pmuic);
|
||||
extern int attach_usb_util(muic_data_t *pmuic,
|
||||
muic_attached_dev_t new_dev);
|
||||
extern int attach_usb(muic_data_t *pmuic,
|
||||
muic_attached_dev_t new_dev);
|
||||
extern int detach_usb(muic_data_t *pmuic);
|
||||
extern int attach_otg_usb(muic_data_t *pmuic,
|
||||
muic_attached_dev_t new_dev);
|
||||
extern int detach_otg_usb(muic_data_t *pmuic);
|
||||
extern int attach_ps_cable(muic_data_t *pmuic,
|
||||
muic_attached_dev_t new_dev);
|
||||
extern int detach_ps_cable(muic_data_t *pmuic);
|
||||
extern int attach_ps_cable(muic_data_t *pmuic,
|
||||
muic_attached_dev_t new_dev);
|
||||
extern int attach_deskdock(muic_data_t *pmuic,
|
||||
muic_attached_dev_t new_dev);
|
||||
extern int detach_deskdock(muic_data_t *pmuic);
|
||||
extern int attach_audiodock(muic_data_t *pmuic,
|
||||
muic_attached_dev_t new_dev, u8 vbus);
|
||||
extern int detach_audiodock(muic_data_t *pmuic);
|
||||
extern int attach_jig_uart_boot_off(muic_data_t *pmuic, muic_attached_dev_t new_dev,
|
||||
u8 vbvolt);
|
||||
extern int detach_jig_uart_boot_off(muic_data_t *pmuic);
|
||||
extern int attach_jig_uart_boot_on(muic_data_t *pmuic, muic_attached_dev_t new_dev);
|
||||
extern int detach_jig_uart_boot_on(muic_data_t *pmuic);
|
||||
extern int attach_jig_usb_boot_off(muic_data_t *pmuic,
|
||||
u8 vbvolt);
|
||||
extern int attach_jig_usb_boot_on(muic_data_t *pmuic,
|
||||
u8 vbvolt);
|
||||
extern int attach_mhl(muic_data_t *pmuic);
|
||||
extern int detach_mhl(muic_data_t *pmuic);
|
||||
extern int get_adc(muic_data_t *pmuic);
|
||||
extern int get_vps_data(muic_data_t *pmuic, void *pdata);
|
||||
extern void set_switch_mode(muic_data_t *pmuic, int mode);
|
||||
extern int get_switch_mode(muic_data_t *pmuic);
|
||||
extern void set_adc_scan_mode(muic_data_t *pmuic,const u8 val);
|
||||
extern int get_adc_scan_mode(muic_data_t *pmuic);
|
||||
|
||||
#ifdef CONFIG_UART_SEL
|
||||
extern int java_muic_set_path(void *drv_data, int path);
|
||||
#endif
|
||||
#endif
|
106
drivers/muic/universal/muic_debug.c
Normal file
106
drivers/muic/universal/muic_debug.c
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* sm5703-muic.c - SM5703 micro USB switch device driver
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics
|
||||
* Thomas Ryu <smilesr.ryu@samsung.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/host_notify.h>
|
||||
|
||||
#include <linux/muic/muic.h>
|
||||
|
||||
#if defined(CONFIG_MUIC_NOTIFIER)
|
||||
#include <linux/muic/muic_notifier.h>
|
||||
#endif /* CONFIG_MUIC_NOTIFIER */
|
||||
|
||||
#if defined (CONFIG_OF)
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
#include "muic-internal.h"
|
||||
#include "muic_debug.h"
|
||||
#include "muic_i2c.h"
|
||||
#include "muic_regmap.h"
|
||||
|
||||
#define MAX_LOG 25
|
||||
|
||||
static u8 muic_log_cnt;
|
||||
static u8 muic_log[MAX_LOG][3];
|
||||
|
||||
void muic_reg_log(u8 reg, u8 value, u8 rw)
|
||||
{
|
||||
muic_log[muic_log_cnt][0]=reg;
|
||||
muic_log[muic_log_cnt][1]=value;
|
||||
muic_log[muic_log_cnt][2]=rw;
|
||||
muic_log_cnt++;
|
||||
if(muic_log_cnt >= MAX_LOG) muic_log_cnt = 0;
|
||||
}
|
||||
void muic_print_reg_log(void)
|
||||
{
|
||||
int i;
|
||||
u8 reg, value, rw;
|
||||
char mesg[256]="";
|
||||
|
||||
for( i = 0 ; i < MAX_LOG ; i++ )
|
||||
{
|
||||
reg = muic_log[muic_log_cnt][0];
|
||||
value = muic_log[muic_log_cnt][1];
|
||||
rw = muic_log[muic_log_cnt][2];
|
||||
muic_log_cnt++;
|
||||
|
||||
if(muic_log_cnt >= MAX_LOG) muic_log_cnt = 0;
|
||||
sprintf(mesg+strlen(mesg),"%x(%x)%x ", reg, value, rw);
|
||||
}
|
||||
pr_info("%s:%s\n", __func__, mesg);
|
||||
}
|
||||
void muic_read_reg_dump(muic_data_t *pmuic, char *mesg)
|
||||
{
|
||||
struct regmap_ops *pops = pmuic->regmapdesc->regmapops;
|
||||
|
||||
pops->get_formatted_dump(pmuic->regmapdesc, mesg);
|
||||
}
|
||||
void muic_print_reg_dump(muic_data_t *pmuic)
|
||||
{
|
||||
char mesg[256]="";
|
||||
|
||||
muic_read_reg_dump(pmuic, mesg);
|
||||
|
||||
pr_info("%s:%s\n", __func__, mesg);
|
||||
}
|
||||
|
||||
void muic_show_debug_info(struct work_struct *work)
|
||||
{
|
||||
muic_data_t *pmuic =
|
||||
container_of(work, muic_data_t, usb_work.work);
|
||||
|
||||
mutex_lock(&pmuic->muic_mutex);
|
||||
muic_print_reg_log();
|
||||
muic_print_reg_dump(pmuic);
|
||||
mutex_unlock(&pmuic->muic_mutex);
|
||||
|
||||
INIT_DELAYED_WORK(&pmuic->usb_work, muic_show_debug_info);
|
||||
schedule_delayed_work(&pmuic->usb_work, msecs_to_jiffies(60000));
|
||||
}
|
14
drivers/muic/universal/muic_debug.h
Normal file
14
drivers/muic/universal/muic_debug.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef _MUIC_DEBUG_
|
||||
#define _MUIC_DEBUG_
|
||||
|
||||
#define DEBUG_MUIC
|
||||
#define READ 0
|
||||
#define WRITE 1
|
||||
|
||||
extern void muic_reg_log(u8 reg, u8 value, u8 rw);
|
||||
extern void muic_print_reg_log(void);
|
||||
extern void muic_read_reg_dump(muic_data_t *muic, char *mesg);
|
||||
extern void muic_print_reg_dump(muic_data_t *pmuic);
|
||||
extern void muic_show_debug_info(struct work_struct *work);
|
||||
|
||||
#endif
|
293
drivers/muic/universal/muic_dt.c
Normal file
293
drivers/muic/universal/muic_dt.c
Normal file
|
@ -0,0 +1,293 @@
|
|||
/*
|
||||
* muic_dt.c
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics
|
||||
* Thomas Ryu <smilesr.ryu@samsung.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/host_notify.h>
|
||||
|
||||
#include <linux/muic/muic.h>
|
||||
|
||||
#if defined(CONFIG_MUIC_NOTIFIER)
|
||||
#include <linux/muic/muic_notifier.h>
|
||||
#endif /* CONFIG_MUIC_NOTIFIER */
|
||||
|
||||
#if defined (CONFIG_OF)
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
#include "muic-internal.h"
|
||||
#include "muic_dt.h"
|
||||
#include "muic_vps.h"
|
||||
|
||||
static int muic_gpio_uart_sel;
|
||||
static bool muic_gpio_uart_ap;
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
struct of_device_id muic_i2c_dt_ids[] = {
|
||||
{ .compatible = "muic-universal" },
|
||||
{ },
|
||||
};
|
||||
#endif
|
||||
|
||||
/* The supported APS list from dts format as follows.
|
||||
+ : MUIC SHOULD detect the device type.
|
||||
It's not MUIC driver's concern whether it is supported or not.
|
||||
- : MUIC SHOULD NOT detect the device type.
|
||||
|
||||
"+OTG:GND",
|
||||
"+MHL:1K",
|
||||
"+VZW Accessory:28.7K",
|
||||
"+VZW Incompatible:34K",
|
||||
"-Smartdock:40.2K",
|
||||
"-HMT:49.9K",
|
||||
"-Audiodock:64.9K",
|
||||
"+USB LANHUB:80.07K",
|
||||
"+Charging Cable:102K",
|
||||
"+Universal Multimedia dock:121K",
|
||||
"+Jig USB Off:255K",
|
||||
"+Jig USB On:301K",
|
||||
"+Deskdock:365K",
|
||||
"+TYPE2 Charger:442K",
|
||||
"+Jig UART Off:523K",
|
||||
"+Jig UART On:619K"
|
||||
"+TA:OPEN",
|
||||
"+USB:OPEN",
|
||||
"+CDP:OPEN",
|
||||
"+Undefined Charging",
|
||||
*/
|
||||
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
int of_update_supported_list(struct i2c_client *i2c,
|
||||
struct muic_platform_data *pdata)
|
||||
{
|
||||
struct device_node *np_muic;
|
||||
const char *prop, *prop_end;
|
||||
char prop_buf[64];
|
||||
int i, prop_num;
|
||||
int ret = 0;
|
||||
int mdev = 0;
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
np_muic = of_find_node_by_path("/muic");
|
||||
if (np_muic == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
prop_num = of_property_count_strings(np_muic, "muic,support-list");
|
||||
if (prop_num < 0) {
|
||||
pr_warn("%s:%s No 'support list dt node'[%d]\n",
|
||||
MUIC_DEV_NAME, __func__, prop_num);
|
||||
ret = prop_num;
|
||||
goto err;
|
||||
}
|
||||
|
||||
memset(prop_buf, 0x00, sizeof(prop_buf));
|
||||
|
||||
for (i = 0; i < prop_num; i++) {
|
||||
ret = of_property_read_string_index(np_muic, "muic,support-list", i,
|
||||
&prop);
|
||||
if (ret) {
|
||||
pr_err("%s:%s Cannot find string at [%d], ret[%d]\n",
|
||||
MUIC_DEV_NAME, __func__, i, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
prop_end = strstr(prop, ":");
|
||||
if (!prop_end) {
|
||||
pr_err("%s: Wrong prop format. %s\n", __func__, prop);
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(prop_buf, prop, prop_end - prop);
|
||||
prop_buf[prop_end - prop] = 0x00;
|
||||
|
||||
if (prop_buf[0] == '+') {
|
||||
if (vps_name_to_mdev(&prop_buf[1], &mdev))
|
||||
vps_update_supported_attr(mdev, true);
|
||||
} else if (prop_buf[0] == '-') {
|
||||
if (vps_name_to_mdev(&prop_buf[1], &mdev))
|
||||
vps_update_supported_attr(mdev, false);
|
||||
} else {
|
||||
pr_err("%s: %c Undefined prop attribute.\n", __func__, prop_buf[0]);
|
||||
}
|
||||
}
|
||||
|
||||
prop_num = of_property_count_strings(np_muic, "muic,support-list-variant");
|
||||
if (prop_num < 0) {
|
||||
pr_warn("%s:%s No support list-variant dt node'[%d]\n",
|
||||
MUIC_DEV_NAME, __func__, prop_num);
|
||||
ret = prop_num;
|
||||
goto err;
|
||||
}
|
||||
|
||||
memset(prop_buf, 0x00, sizeof(prop_buf));
|
||||
|
||||
for (i = 0; i < prop_num; i++) {
|
||||
ret = of_property_read_string_index(np_muic, "muic,support-list-variant", i,
|
||||
&prop);
|
||||
if (ret) {
|
||||
pr_err("%s:%s Cannot find variant-string at [%d], ret[%d]\n",
|
||||
MUIC_DEV_NAME, __func__, i, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
prop_end = strstr(prop, ":");
|
||||
if (!prop_end) {
|
||||
pr_err("%s: Wrong prop format. %s\n", __func__, prop);
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(prop_buf, prop, prop_end - prop);
|
||||
prop_buf[prop_end - prop] = 0x00;
|
||||
|
||||
if (prop_buf[0] == '+') {
|
||||
if (vps_name_to_mdev(&prop_buf[1], &mdev))
|
||||
vps_update_supported_attr(mdev, true);
|
||||
} else if (prop_buf[0] == '-') {
|
||||
if (vps_name_to_mdev(&prop_buf[1], &mdev))
|
||||
vps_update_supported_attr(mdev, false);
|
||||
} else {
|
||||
pr_err("%s: %c Undefined prop attribute.\n", __func__, prop_buf[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
err:
|
||||
of_node_put(np_muic);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int of_muic_dt(struct i2c_client *i2c, struct muic_platform_data *pdata)
|
||||
{
|
||||
struct device_node *np_muic = i2c->dev.of_node;
|
||||
muic_data_t *pmuic = i2c_get_clientdata(i2c);
|
||||
int ret=0;
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
if(!np_muic)
|
||||
return -EINVAL;
|
||||
|
||||
ret = of_property_read_string(np_muic,
|
||||
"muic-universal,chip_name", (char const **)&pmuic->chip_name);
|
||||
if (ret)
|
||||
pr_info("%s: Vendor is Empty\n", __func__);
|
||||
else
|
||||
pr_info("%s: chip_name is %s\n", __func__, pmuic->chip_name);
|
||||
|
||||
pdata->irq_gpio = of_get_named_gpio(np_muic, "muic-universal,irq-gpio", 0);
|
||||
pr_info("%s: irq-gpio: %u\n", __func__, pdata->irq_gpio);
|
||||
|
||||
#if 0
|
||||
muic_gpio_uart_sel = of_get_named_gpio(np_muic, "muic-universal,uart-gpio", 0);
|
||||
ret = gpio_is_valid(muic_gpio_uart_sel);
|
||||
if (!ret) {
|
||||
pr_err("GPIO_UART_SEL is not valid!!!\n");
|
||||
pmuic->gpio_uart_sel = muic_gpio_uart_sel = 0;
|
||||
return ret;
|
||||
} else {
|
||||
ret = gpio_request(muic_gpio_uart_sel, "GPIO_UART_SEL");
|
||||
if (ret) {
|
||||
pr_err("failed to gpio_request GPIO_UART_SEL\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
muic_gpio_uart_ap = of_property_read_bool(np_muic, "muic-universal,uart-ap");
|
||||
|
||||
pr_info("%s: uart-gpio : %d\n", __func__, muic_gpio_uart_sel);
|
||||
pr_info("%s: uart-ap : %d\n", __func__, muic_gpio_uart_ap);
|
||||
}
|
||||
|
||||
pmuic->gpio_uart_sel = muic_gpio_uart_sel;
|
||||
#endif
|
||||
pmuic->gpio_uart_sel = muic_gpio_uart_sel = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#if defined(CONFIG_MUIC_PINCTRL)
|
||||
int of_muic_pinctrl(struct i2c_client *i2c)
|
||||
{
|
||||
struct pinctrl *muic_pinctrl;
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
muic_pinctrl = devm_pinctrl_get_select(&i2c->dev, "muic_i2c_pins_default");
|
||||
if (IS_ERR(muic_pinctrl)) {
|
||||
if (PTR_ERR(muic_pinctrl) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
pr_debug("Target does not use i2c pinctrl\n");
|
||||
muic_pinctrl = NULL;
|
||||
}
|
||||
muic_pinctrl = devm_pinctrl_get_select(&i2c->dev, "muic_interrupt_pins_default");
|
||||
if (IS_ERR(muic_pinctrl)) {
|
||||
if (PTR_ERR(muic_pinctrl) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
pr_debug("Target does not use int pinctrl\n");
|
||||
muic_pinctrl = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
int muic_set_gpio_uart_sel(int uart_sel)
|
||||
{
|
||||
const char *mode;
|
||||
int uart_sel_gpio = muic_gpio_uart_sel;
|
||||
|
||||
if (!uart_sel_gpio) {
|
||||
pr_err("%s: No UART gpio defined.\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (uart_sel) {
|
||||
case MUIC_PATH_UART_AP:
|
||||
mode = "AP_UART";
|
||||
if (gpio_is_valid(uart_sel_gpio))
|
||||
gpio_direction_output(uart_sel_gpio, muic_gpio_uart_ap);
|
||||
break;
|
||||
case MUIC_PATH_UART_CP:
|
||||
mode = "CP_UART";
|
||||
if (gpio_is_valid(uart_sel_gpio))
|
||||
gpio_direction_output(uart_sel_gpio, !muic_gpio_uart_ap);
|
||||
break;
|
||||
default:
|
||||
mode = "Error";
|
||||
break;
|
||||
}
|
||||
|
||||
pr_info("%s: uart_sel(%d), GPIO_UART_SEL(%d)=%s", __func__, uart_sel,
|
||||
uart_sel_gpio, mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
13
drivers/muic/universal/muic_dt.h
Normal file
13
drivers/muic/universal/muic_dt.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef _MUIC_DT_
|
||||
#define _MUIC_DT_
|
||||
|
||||
extern struct of_device_id muic_i2c_dt_ids[];
|
||||
|
||||
extern int of_update_supported_list(struct i2c_client *i2c,
|
||||
struct muic_platform_data *pdata);
|
||||
extern int of_muic_dt(struct i2c_client *i2c, struct muic_platform_data *pdata);
|
||||
#if defined(CONFIG_MUIC_PINCTRL)
|
||||
extern int of_muic_pinctrl(struct i2c_client *i2c);
|
||||
#endif
|
||||
extern int muic_set_gpio_uart_sel(int uart_sel);
|
||||
#endif
|
118
drivers/muic/universal/muic_i2c.c
Normal file
118
drivers/muic/universal/muic_i2c.c
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* muic_i2c.c - SM5703 micro USB switch device driver
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics
|
||||
* Thomas Ryu <smilesr.ryu@samsung.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/host_notify.h>
|
||||
|
||||
#include <linux/muic/muic.h>
|
||||
|
||||
#if defined(CONFIG_MUIC_NOTIFIER)
|
||||
#include <linux/muic/muic_notifier.h>
|
||||
#endif /* CONFIG_MUIC_NOTIFIER */
|
||||
|
||||
#if defined (CONFIG_OF)
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
#include "muic-internal.h"
|
||||
#include "muic_debug.h"
|
||||
|
||||
int muic_i2c_read_byte(const struct i2c_client *client, u8 command)
|
||||
{
|
||||
int ret;
|
||||
int retry = 0;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, command);
|
||||
|
||||
while(ret < 0){
|
||||
pr_err("%s:i2c err on reading reg(0x%x), retrying ...\n",
|
||||
__func__, command);
|
||||
if(retry > 10)
|
||||
{
|
||||
pr_err("%s: retry count > 10 : failed !!\n", __func__);
|
||||
break;
|
||||
}
|
||||
msleep(100);
|
||||
ret = i2c_smbus_read_byte_data(client, command);
|
||||
retry ++;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_MUIC
|
||||
muic_reg_log(command, ret, retry << 1| READ);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
int muic_i2c_write_byte(const struct i2c_client *client,
|
||||
u8 command, u8 value)
|
||||
{
|
||||
int ret;
|
||||
int retry = 0;
|
||||
int written = 0;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, command, value);
|
||||
|
||||
while(ret < 0) {
|
||||
written = i2c_smbus_read_byte_data(client, command);
|
||||
if(written < 0) pr_err("%s:i2c err on reading reg(0x%x)\n",
|
||||
__func__, command);
|
||||
msleep(100);
|
||||
ret = i2c_smbus_write_byte_data(client, command, value);
|
||||
retry ++;
|
||||
}
|
||||
#ifdef DEBUG_MUIC
|
||||
muic_reg_log(command, value, retry << 1| WRITE);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
int muic_i2c_guaranteed_wbyte(const struct i2c_client *client,
|
||||
u8 command, u8 value)
|
||||
{
|
||||
int ret;
|
||||
int retry = 0;
|
||||
int written;
|
||||
|
||||
ret = muic_i2c_write_byte(client, command, value);
|
||||
written = muic_i2c_read_byte(client, command);
|
||||
|
||||
while(written != value){
|
||||
pr_err("%s:reg(0x%x): written(0x%x) != value(0x%x)...\n",
|
||||
__func__, command, written, value);
|
||||
if(retry > 10)
|
||||
{
|
||||
pr_err("%s: retry count > 10 : failed !!\n", __func__);
|
||||
break;
|
||||
}
|
||||
msleep(100);
|
||||
retry ++;
|
||||
ret = muic_i2c_write_byte(client, command, value);
|
||||
written = muic_i2c_read_byte(client, command);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
9
drivers/muic/universal/muic_i2c.h
Normal file
9
drivers/muic/universal/muic_i2c.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#ifndef _MUIC_I2C_
|
||||
#define _MUIC_I2C_
|
||||
|
||||
extern int muic_i2c_read_byte(const struct i2c_client *client, u8 command);
|
||||
extern int muic_i2c_write_byte(const struct i2c_client *client,
|
||||
u8 command, u8 value);
|
||||
extern int muic_i2c_guaranteed_wbyte(const struct i2c_client *client,
|
||||
u8 command, u8 value);
|
||||
#endif
|
401
drivers/muic/universal/muic_regmap.c
Normal file
401
drivers/muic/universal/muic_regmap.c
Normal file
|
@ -0,0 +1,401 @@
|
|||
/*
|
||||
* muic_regmap.c
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics
|
||||
* Thomas Ryu <smilesr.ryu@samsung.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/host_notify.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <linux/muic/muic.h>
|
||||
|
||||
#if defined(CONFIG_MUIC_NOTIFIER)
|
||||
#include <linux/muic/muic_notifier.h>
|
||||
#endif /* CONFIG_MUIC_NOTIFIER */
|
||||
|
||||
#if defined (CONFIG_OF)
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
#include "muic-internal.h"
|
||||
#include "muic_i2c.h"
|
||||
#include "muic_regmap.h"
|
||||
|
||||
struct vendor_regmap {
|
||||
char *name;
|
||||
void (*func)(struct regmap_desc **);
|
||||
};
|
||||
|
||||
char *regmap_to_name(struct regmap_desc *pdesc, int addr)
|
||||
{
|
||||
if (addr >= pdesc->size) {
|
||||
pr_err("%s out of addr range:%d\n", __func__, addr);
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
return pdesc->regmap[addr].name;
|
||||
}
|
||||
|
||||
int regmap_write_value(struct regmap_desc *pdesc, int uattr, int value)
|
||||
{
|
||||
struct i2c_client *i2c = pdesc->muic->i2c;
|
||||
struct reg_attr attr;
|
||||
int curr, result = 0;
|
||||
u8 reg_val;
|
||||
|
||||
_REG_ATTR(&attr, uattr);
|
||||
|
||||
if (pdesc->trace)
|
||||
pr_info("%s %s[%02x]:%02x<<%d, %02x\n", __func__,
|
||||
regmap_to_name(pdesc, attr.addr),
|
||||
attr.addr, attr.mask, attr.bitn, value);
|
||||
|
||||
curr = muic_i2c_read_byte(i2c, attr.addr);
|
||||
if (curr < 0)
|
||||
goto i2c_read_error;
|
||||
|
||||
if (uattr & _ATTR_OVERWRITE_M) {
|
||||
reg_val = value;
|
||||
} else {
|
||||
reg_val = curr & ~(attr.mask << attr.bitn);
|
||||
reg_val |= ((value & attr.mask) << attr.bitn);
|
||||
}
|
||||
|
||||
if (reg_val ^ curr) {
|
||||
if (muic_i2c_guaranteed_wbyte(i2c, attr.addr, reg_val) < 0)
|
||||
goto i2c_write_error;
|
||||
|
||||
result = muic_i2c_read_byte(i2c, attr.addr);
|
||||
if (result < 0)
|
||||
goto i2c_read_error;
|
||||
|
||||
if (pdesc->trace)
|
||||
pr_info(" -%s done %02x+%02x->%02x(=%02x)\n",
|
||||
(uattr & _ATTR_OVERWRITE_M) ?
|
||||
"Overwrite" : "Update",
|
||||
curr, value, reg_val, result);
|
||||
} else {
|
||||
result = reg_val;
|
||||
|
||||
if (pdesc->trace)
|
||||
pr_info(" -%s skip %02x+%02x->%02x(=%02x)\n",
|
||||
(uattr & _ATTR_OVERWRITE_M) ?
|
||||
"Overwrite" : "Update",
|
||||
curr, value, reg_val, result);
|
||||
}
|
||||
|
||||
return result | ((curr << 8) & 0xff00);
|
||||
|
||||
i2c_write_error:
|
||||
pr_err("%s i2c write error.\n", __func__);
|
||||
return -1;
|
||||
|
||||
i2c_read_error:
|
||||
pr_err("%s i2c read error.\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* read a shifed masked value */
|
||||
int regmap_read_value(struct regmap_desc *pdesc, int uattr)
|
||||
{
|
||||
struct reg_attr attr;
|
||||
int s_m_value = 0, curr;
|
||||
|
||||
_REG_ATTR(&attr, uattr);
|
||||
curr = muic_i2c_read_byte(pdesc->muic->i2c, attr.addr);
|
||||
if (curr < 0)
|
||||
pr_err("%s err read %s(%d)\n", __func__,
|
||||
regmap_to_name(pdesc, attr.addr), curr);
|
||||
|
||||
s_m_value = curr >> attr.bitn;
|
||||
s_m_value &= attr.mask;
|
||||
|
||||
if (pdesc->trace)
|
||||
pr_info("%s %02x/%02x/%02x %02x->%02x\n", __func__,
|
||||
attr.addr, attr.mask, attr.bitn, curr, s_m_value);
|
||||
|
||||
return s_m_value;
|
||||
}
|
||||
|
||||
int regmap_read_raw_value(struct regmap_desc *pdesc, int uattr)
|
||||
{
|
||||
struct reg_attr attr;
|
||||
int curr;
|
||||
|
||||
_REG_ATTR(&attr, uattr);
|
||||
curr = muic_i2c_read_byte(pdesc->muic->i2c, attr.addr);
|
||||
if (curr < 0)
|
||||
pr_err("%s err read %s(%d)\n", __func__,
|
||||
regmap_to_name(pdesc, attr.addr), curr);
|
||||
|
||||
if (pdesc->trace)
|
||||
pr_info("%s %02x/%02x/%02x %02x\n", __func__,
|
||||
attr.addr, attr.mask, attr.bitn, curr);
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
int regmap_com_to(struct regmap_desc *pdesc, int port)
|
||||
{
|
||||
struct regmap_ops *pops = pdesc->regmapops;
|
||||
int uattr, ret;
|
||||
|
||||
ret = pops->ioctl(pdesc, GET_COM_VAL, &port, &uattr);
|
||||
if (ret < 0) {
|
||||
pr_info("[muic] %s autoconfig mode\n", __func__);
|
||||
ret = 0;
|
||||
} else {
|
||||
uattr |= _ATTR_OVERWRITE_M;
|
||||
ret = regmap_write_value(pdesc, uattr, port);
|
||||
|
||||
_REGMAP_TRACE(pdesc, 'w', ret, uattr, port);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int muic_reg_init(muic_data_t *pmuic)
|
||||
{
|
||||
struct regmap_ops *pops = pmuic->regmapdesc->regmapops;
|
||||
|
||||
return pops->init(pmuic->regmapdesc);
|
||||
}
|
||||
|
||||
int set_int_mask(muic_data_t *pmuic, bool on)
|
||||
{
|
||||
struct regmap_ops *pops = pmuic->regmapdesc->regmapops;
|
||||
int uattr, ret;
|
||||
|
||||
pops->ioctl(pmuic->regmapdesc, GET_INT_MASK, NULL, &uattr);
|
||||
ret = regmap_write_value(pmuic->regmapdesc, uattr, on);
|
||||
|
||||
_REGMAP_TRACE(pmuic->regmapdesc, 'w', ret, uattr, on);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int muic_init_chip(struct regmap_desc *pdesc)
|
||||
{
|
||||
regmap_t *preg = pdesc->regmap;
|
||||
int i;
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
for (i = 0; i < pdesc->size; i++, preg++) {
|
||||
if (!preg->name)
|
||||
continue;
|
||||
|
||||
if (preg->init != INIT_NONE) {
|
||||
pr_info(" [%02x] : 0x%02x\n", i, preg->init);
|
||||
if (muic_i2c_write_byte(pdesc->muic->i2c,
|
||||
i, preg->init) < 0)
|
||||
goto i2c_write_error;
|
||||
}
|
||||
else if(preg->init == INIT_INT_CLR){
|
||||
pr_info("%s:%s clear MUIC interrupt %x registers\n",MUIC_DEV_NAME, __func__,i);
|
||||
muic_i2c_read_byte(pdesc->muic->i2c, i);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
i2c_write_error:
|
||||
pr_err("%s i2c write error.\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int muic_rev_info(struct regmap_desc *pdesc)
|
||||
{
|
||||
struct regmap_ops *pops = pdesc->regmapops;
|
||||
muic_data_t *pmuic = pdesc->muic;
|
||||
struct reg_attr attr;
|
||||
int uattr;
|
||||
|
||||
pops->ioctl(pmuic->regmapdesc, GET_REVISION, NULL, &uattr);
|
||||
_REG_ATTR(&attr, uattr);
|
||||
|
||||
uattr = muic_i2c_read_byte(pmuic->i2c, attr.addr);
|
||||
if (uattr < 0) {
|
||||
pr_err("%s i2c io error(%d)\n", __func__, uattr);
|
||||
return -ENODEV;
|
||||
} else {
|
||||
uattr &= (attr.mask << attr.bitn);
|
||||
pmuic->muic_vendor = (uattr & 0x7);
|
||||
pmuic->muic_version = ((uattr & 0xF8) >> 3);
|
||||
pr_info("%s [%s] vendor=0x%x, ver=0x%x\n",
|
||||
__func__, regmap_to_name(pdesc, attr.addr),
|
||||
pmuic->muic_vendor, pmuic->muic_version);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int muic_reset_chip(struct regmap_desc *pdesc)
|
||||
{
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int muic_update_regmapdata(struct regmap_desc *pdesc, int size)
|
||||
{
|
||||
muic_data_t *pmuic = pdesc->muic;
|
||||
regmap_t *preg = pdesc->regmap;
|
||||
int ret = 0, i;
|
||||
|
||||
pr_info("%s REG_END:%d size:%d\n", __func__, pdesc->size, size);
|
||||
|
||||
for (i = 0; i < pdesc->size; i++, preg++) {
|
||||
if (!preg->name
|
||||
#ifdef CONFIG_MUIC_UNIVERSAL_SM5705
|
||||
|| !strcmp(preg->name,"INT1") //Do not read int reg because of AFC_ATTACH int.
|
||||
|| !strcmp(preg->name,"INT2")
|
||||
|| !strcmp(preg->name,"INT3_AFC")
|
||||
#endif
|
||||
) {
|
||||
pr_info("pass init register : %s\n", preg->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = muic_i2c_read_byte(pmuic->i2c, i);
|
||||
if (ret < 0)
|
||||
pr_err("%s i2c read error(%d)\n", __func__, ret);
|
||||
else
|
||||
preg->curr = ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int muic_show_regmapdata(struct regmap_desc *pdesc, regmap_t *p)
|
||||
{
|
||||
char buf[128];
|
||||
int i;
|
||||
|
||||
memset(buf, 0x00, sizeof(buf));
|
||||
|
||||
pr_info("\n");
|
||||
sprintf(buf, " %4s %16s %4s %4s %4s",
|
||||
"Addr", "Name", " Rst", "Curr", "Init");
|
||||
pr_info("%s\n", buf);
|
||||
pr_info("----------------------------------\n");
|
||||
for (i = 0; i < pdesc->size; i++, p++) {
|
||||
if (!p->name)
|
||||
continue;
|
||||
|
||||
sprintf(buf, " 0x%02x %16s 0x%02x 0x%02x 0x%02x", i,
|
||||
p->name, p->reset, p->curr, p->init);
|
||||
pr_info("%s\n", buf);
|
||||
}
|
||||
|
||||
pr_info("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int muic_update_regmap(struct regmap_desc *pdesc)
|
||||
{
|
||||
return muic_update_regmapdata(pdesc,
|
||||
pdesc->regmapops->get_size());
|
||||
}
|
||||
|
||||
static void muic_show_regmap(struct regmap_desc *pdesc)
|
||||
{
|
||||
muic_show_regmapdata(pdesc, pdesc->regmap);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_SM5504)
|
||||
extern void muic_register_sm5504_regmap_desc(struct regmap_desc **pdesc);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_SM5703)
|
||||
extern void muic_register_sm5703_regmap_desc(struct regmap_desc **pdesc);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_SM5705)
|
||||
extern void muic_register_sm5705_regmap_desc(struct regmap_desc **pdesc);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_S2MM001)
|
||||
extern void muic_register_s2mm001_regmap_desc(struct regmap_desc **pdesc);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_MAX77849)
|
||||
extern void muic_register_max77849_regmap_desc(struct regmap_desc **pdesc);
|
||||
#endif
|
||||
|
||||
static struct vendor_regmap vendor_regmap_tbl[] = {
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_SM5504)
|
||||
{"sm,sm5504", muic_register_sm5504_regmap_desc},
|
||||
#endif
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_SM5703)
|
||||
{"sm,sm5703", muic_register_sm5703_regmap_desc},
|
||||
#endif
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_SM5705)
|
||||
{"sm,sm5705", muic_register_sm5705_regmap_desc},
|
||||
#endif
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_S2MM001)
|
||||
{"lsi,s2mm001", muic_register_s2mm001_regmap_desc},
|
||||
#endif
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_MAX77849)
|
||||
{"max,max77849", muic_register_max77849_regmap_desc},
|
||||
#endif
|
||||
{"", NULL},
|
||||
};
|
||||
void muic_register_regmap(struct regmap_desc **pdesc, void *pdata)
|
||||
{
|
||||
struct regmap_desc *pdesc_temp = NULL;
|
||||
struct regmap_ops *pops;
|
||||
struct vendor_regmap *pvtbl = vendor_regmap_tbl;
|
||||
muic_data_t *pmuic = (muic_data_t *)pdata;
|
||||
int i;
|
||||
|
||||
/* Get a chipset descriptor */
|
||||
pr_info("chip_name : %s\n",pmuic->chip_name);
|
||||
for (i = 0; pvtbl->name; i++, pvtbl++) {
|
||||
pr_info(" [%d] : %s\n", i, pvtbl->name);
|
||||
if (!strcmp(pvtbl->name, pmuic->chip_name)) {
|
||||
pvtbl->func(&pdesc_temp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == sizeof(vendor_regmap_tbl)/sizeof(struct vendor_regmap)) {
|
||||
pr_info("%s: No matched regmap driver.\n", __func__);
|
||||
}
|
||||
|
||||
pr_info("%s: %s registered.\n", __func__, pdesc_temp->name);
|
||||
|
||||
*pdesc = pdesc_temp;
|
||||
pops = pdesc_temp->regmapops;
|
||||
|
||||
/* Add generic functions */
|
||||
pops->init = muic_init_chip;
|
||||
pops->revision = muic_rev_info;
|
||||
pops->reset = muic_reset_chip;
|
||||
pops->show = muic_show_regmap;
|
||||
pops->update = muic_update_regmap;
|
||||
}
|
143
drivers/muic/universal/muic_regmap.h
Normal file
143
drivers/muic/universal/muic_regmap.h
Normal file
|
@ -0,0 +1,143 @@
|
|||
#ifndef _MUIC_REGMAP_
|
||||
#define _MUIC_REGMAP_
|
||||
|
||||
#define _MASK0 0x00
|
||||
#define _MASK1 0x01
|
||||
#define _MASK2 0x03
|
||||
#define _MASK3 0x07
|
||||
#define _MASK4 0x0f
|
||||
#define _MASK5 0x1f
|
||||
#define _MASK6 0x3f
|
||||
#define _MASK7 0x7f
|
||||
#define _MASK8 0xff
|
||||
#define MASK(n) _MASK##n
|
||||
|
||||
#define _BIT0 0
|
||||
#define _BIT1 1
|
||||
#define _BIT2 2
|
||||
#define _BIT3 3
|
||||
#define _BIT4 4
|
||||
#define _BIT5 5
|
||||
#define _BIT6 6
|
||||
#define _BIT7 7
|
||||
|
||||
#define BITN(nr) (nr)
|
||||
|
||||
#define INIT_NONE (-1)
|
||||
#define INIT_INT_CLR (-2)
|
||||
|
||||
#define _REGMAP_TRACE(d, rw, r, a, v) do { \
|
||||
pr_info(" -muic_regmap_trace %c %02x=[%02x:%02x+%08x]%s\n", \
|
||||
rw, r&0xff, (r>>8)&0xff, v, a, \
|
||||
regmap_to_name(d, _ATTR_ADDR(a))); \
|
||||
} while (0)
|
||||
|
||||
|
||||
struct reg_attr;
|
||||
|
||||
#define _REG_ATTR(x, y) do { \
|
||||
((struct reg_attr *)x)->value = (y>>24) & 0xff; \
|
||||
((struct reg_attr *)x)->bitn = (y>>16) & 0x0f; \
|
||||
((struct reg_attr *)x)->mask = (y>>8) & 0xff; \
|
||||
((struct reg_attr *)x)->addr = y & 0xff; \
|
||||
} while (0)
|
||||
|
||||
#define _ATTR_VALUE(x) ((x>>24) & 0xff)
|
||||
#define _ATTR_BITP(x) ((x>>16) & 0x0f)
|
||||
#define _ATTR_ATTR(x) ((x>>20) & 0x0f)
|
||||
#define _ATTR_MASK(x) ((x>>8) & 0xff)
|
||||
#define _ATTR_ADDR(x) (x & 0xff)
|
||||
|
||||
#define _ATTR_VALUE_BITP 24
|
||||
/*
|
||||
* xxxo
|
||||
* o : update(0) or overwirte(1)
|
||||
*/
|
||||
|
||||
#define _ATTR_OVERWRITE_M (1 << 20)
|
||||
|
||||
/* Interrupt type */
|
||||
enum {
|
||||
INT_REQ_ATTACH = 1<<0,
|
||||
INT_REQ_DETACH = 1<<1,
|
||||
INT_REQ_OVP = 1<<2,
|
||||
INT_REQ_RESET = 1<<3,
|
||||
INT_REQ_DONE = 1<<4,
|
||||
INT_REQ_DISCARD = 1<<5,
|
||||
};
|
||||
|
||||
struct reg_attr {
|
||||
u8 value;
|
||||
u8 bitn;
|
||||
u8 mask;
|
||||
u8 addr;
|
||||
};
|
||||
|
||||
typedef struct _regmap_t {
|
||||
char *name;
|
||||
u8 reset;
|
||||
u8 curr;
|
||||
int init;
|
||||
} regmap_t;
|
||||
|
||||
|
||||
struct regmap_desc;
|
||||
|
||||
struct regmap_ops {
|
||||
int (*get_size)(void);
|
||||
int (*init)(struct regmap_desc *);
|
||||
int (*revision)(struct regmap_desc *);
|
||||
int (*reset)(struct regmap_desc *);
|
||||
void (*show)(struct regmap_desc *);
|
||||
int (*update)(struct regmap_desc *);
|
||||
int (*ioctl)(struct regmap_desc *pdesc, int arg1, int *arg2, int *arg3);
|
||||
void (*get_formatted_dump)(struct regmap_desc *pdesc, char *mesg);
|
||||
};
|
||||
|
||||
struct vendor_ops {
|
||||
int (*attach_ta)(struct regmap_desc *);
|
||||
int (*detach_ta)(struct regmap_desc *);
|
||||
int (*get_switch)(struct regmap_desc *);
|
||||
void (*set_switch)(struct regmap_desc *, int);
|
||||
void (*set_adc_scan_mode)(struct regmap_desc *, int);
|
||||
int (*get_adc_scan_mode)(struct regmap_desc *);
|
||||
int (*set_rustproof)(struct regmap_desc *, int);
|
||||
int (*set_manual_JIGON)(struct regmap_desc *, int);
|
||||
int (*get_vps_data)(struct regmap_desc *, void *);
|
||||
int (*muic_enable_accdet)(struct regmap_desc *);
|
||||
int (*muic_disable_accdet)(struct regmap_desc *);
|
||||
int (*rescan)(struct regmap_desc *, int);
|
||||
};
|
||||
|
||||
struct afc_ops {
|
||||
int (*afc_init)(struct regmap_desc *);
|
||||
int (*afc_ta_attach)(struct regmap_desc *);
|
||||
int (*afc_ta_accept)(struct regmap_desc *);
|
||||
int (*afc_vbus_update)(struct regmap_desc *);
|
||||
int (*afc_multi_byte)(struct regmap_desc *);
|
||||
int (*afc_error)(struct regmap_desc *);
|
||||
int (*afc_ctrl_reg)(struct regmap_desc *, int, bool);
|
||||
int (*afc_init_check)(struct regmap_desc *);
|
||||
};
|
||||
|
||||
struct regmap_desc {
|
||||
const char *name;
|
||||
regmap_t *regmap;
|
||||
int size;
|
||||
int trace;
|
||||
struct regmap_ops *regmapops;
|
||||
struct vendor_ops *vendorops;
|
||||
struct afc_ops *afcops;
|
||||
muic_data_t *muic;
|
||||
};
|
||||
|
||||
extern char *regmap_to_name(struct regmap_desc *pdesc, int);
|
||||
extern int regmap_com_to(struct regmap_desc *pdesc, int);
|
||||
extern int muic_reg_init(muic_data_t *pmuic);
|
||||
extern int set_int_mask(muic_data_t *pmuic, bool);
|
||||
extern int set_manual_sw(muic_data_t *pmuic, bool);
|
||||
extern int regmap_read_value(struct regmap_desc *pdesc, int);
|
||||
extern int regmap_read_raw_value(struct regmap_desc *pdesc, int);
|
||||
extern int regmap_write_value(struct regmap_desc *pdesc, int, int);
|
||||
extern void muic_register_regmap(struct regmap_desc **pdesc, void *);
|
||||
#endif
|
593
drivers/muic/universal/muic_regmap_sm5504.c
Normal file
593
drivers/muic/universal/muic_regmap_sm5504.c
Normal file
|
@ -0,0 +1,593 @@
|
|||
/*
|
||||
* muic_regmap_sm5504.c
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics
|
||||
* Thomas Ryu <smilesr.ryu@samsung.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/host_notify.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <linux/muic/muic.h>
|
||||
|
||||
#if defined(CONFIG_MUIC_NOTIFIER)
|
||||
#include <linux/muic/muic_notifier.h>
|
||||
#endif /* CONFIG_MUIC_NOTIFIER */
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
#include "muic-internal.h"
|
||||
#include "muic_i2c.h"
|
||||
#include "muic_regmap.h"
|
||||
|
||||
enum sm5504_muic_reg_init_value {
|
||||
REG_INTMASK1_VALUE = (0xDC),
|
||||
REG_INTMASK2_VALUE = (0x00),
|
||||
};
|
||||
|
||||
/* sm5504 I2C registers */
|
||||
enum sm5504_muic_reg {
|
||||
REG_DEVID = 0x01,
|
||||
REG_CTRL = 0x02,
|
||||
REG_INT1 = 0x03,
|
||||
REG_INT2 = 0x04,
|
||||
REG_INTMASK1 = 0x05,
|
||||
REG_INTMASK2 = 0x06,
|
||||
REG_ADC = 0x07,
|
||||
/* unused registers */
|
||||
REG_DEVT1 = 0x0a,
|
||||
REG_DEVT2 = 0x0b,
|
||||
|
||||
REG_MANSW1 = 0x13,
|
||||
REG_MANSW2 = 0x14,
|
||||
REG_RESET = 0x1B,
|
||||
REG_SET = 0x20,
|
||||
/* 0x22 is reserved. */
|
||||
REG_CHGTYPE = 0x24,
|
||||
REG_RSVDID4 = 0x3A,
|
||||
REG_END,
|
||||
};
|
||||
|
||||
#define REG_ITEM(addr, bitp, mask) ((bitp<<16) | (mask<<8) | addr)
|
||||
|
||||
/* Field */
|
||||
enum sm5504_muic_reg_item {
|
||||
DEVID_VendorID = REG_ITEM(REG_DEVID, _BIT0, _MASK3),
|
||||
|
||||
CTRL_USBCHDEN = REG_ITEM(REG_CTRL, _BIT6, _MASK1),
|
||||
CTRL_SW_OPEN = REG_ITEM(REG_CTRL, _BIT4, _MASK1),
|
||||
CTRL_RAWDATA = REG_ITEM(REG_CTRL, _BIT3, _MASK1),
|
||||
CTRL_ManualSW = REG_ITEM(REG_CTRL, _BIT2, _MASK1),
|
||||
CTRL_MASK_INT = REG_ITEM(REG_CTRL, _BIT0, _MASK1),
|
||||
|
||||
INT1_ADC_CHG = REG_ITEM(REG_INT1, _BIT6, _MASK1),
|
||||
INT1_CONNECT = REG_ITEM(REG_INT1, _BIT5, _MASK1),
|
||||
INT1_OVP_EVENT = REG_ITEM(REG_INT1, _BIT4, _MASK1),
|
||||
INT1_DCD_OUT = REG_ITEM(REG_INT1, _BIT3, _MASK1),
|
||||
INT1_CHG_DET = REG_ITEM(REG_INT1, _BIT2, _MASK1),
|
||||
INT1_DETACH = REG_ITEM(REG_INT1, _BIT1, _MASK1),
|
||||
INT1_ATTACH = REG_ITEM(REG_INT1, _BIT0, _MASK1),
|
||||
|
||||
INT2_OVP_OCP = REG_ITEM(REG_INT2, _BIT7, _MASK1),
|
||||
INT2_OCP = REG_ITEM(REG_INT2, _BIT6, _MASK1),
|
||||
INT2_OCP_LATCH = REG_ITEM(REG_INT2, _BIT5, _MASK1),
|
||||
INT2_OVP_FET = REG_ITEM(REG_INT2, _BIT4, _MASK1),
|
||||
INT2_ROP = REG_ITEM(REG_INT2, _BIT2, _MASK1),
|
||||
INT2_UVLO = REG_ITEM(REG_INT2, _BIT1, _MASK1),
|
||||
INT2_RID_CHG = REG_ITEM(REG_INT2, _BIT0, _MASK1),
|
||||
|
||||
INTMASK1_ADC_CHG = REG_ITEM(REG_INTMASK1, _BIT6, _MASK1),
|
||||
INTMASK1_CONNECT = REG_ITEM(REG_INTMASK1, _BIT5, _MASK1),
|
||||
INTMASK1_OVP_EVENT = REG_ITEM(REG_INTMASK1, _BIT4, _MASK1),
|
||||
INTMASK1_DCD_OUT = REG_ITEM(REG_INTMASK1, _BIT3, _MASK1),
|
||||
INTMASK1_CHG_DET = REG_ITEM(REG_INTMASK1, _BIT2, _MASK1),
|
||||
INTMASK1_DETACH = REG_ITEM(REG_INTMASK1, _BIT1, _MASK1),
|
||||
INTMASK1_ATTACH = REG_ITEM(REG_INTMASK1, _BIT0, _MASK1),
|
||||
|
||||
INTMASK2_OVP_OCP = REG_ITEM(REG_INTMASK2, _BIT7, _MASK1),
|
||||
INTMASK2_OCP = REG_ITEM(REG_INTMASK2, _BIT6, _MASK1),
|
||||
INTMASK2_OCP_LATCH = REG_ITEM(REG_INTMASK2, _BIT5, _MASK1),
|
||||
INTMASK2_OVP_FET = REG_ITEM(REG_INTMASK2, _BIT4, _MASK1),
|
||||
INTMASK2_ROP = REG_ITEM(REG_INTMASK2, _BIT2, _MASK1),
|
||||
INTMASK2_UVLO = REG_ITEM(REG_INTMASK2, _BIT1, _MASK1),
|
||||
INTMASK2_RID_CHG = REG_ITEM(REG_INTMASK2, _BIT0, _MASK1),
|
||||
|
||||
ADC_VALUE = REG_ITEM(REG_ADC, _BIT0, _MASK5),
|
||||
|
||||
DEVT1_DEDICATED_CHG = REG_ITEM(REG_DEVT1, _BIT6, _MASK1),
|
||||
DEVT1_USB_CHG = REG_ITEM(REG_DEVT1, _BIT5, _MASK1),
|
||||
DEVT1_CAR_KIT_CHARGER = REG_ITEM(REG_DEVT1, _BIT4, _MASK1),
|
||||
DEVT1_UART = REG_ITEM(REG_DEVT1, _BIT3, _MASK1),
|
||||
DEVT1_USB = REG_ITEM(REG_DEVT1, _BIT2, _MASK1),
|
||||
DEVT1_USB_OTG = REG_ITEM(REG_DEVT1, _BIT0, _MASK1),
|
||||
|
||||
DEVT2_UNKOWN = REG_ITEM(REG_DEVT2, _BIT6, _MASK1),
|
||||
DEVT2_JIG_UART_OFF = REG_ITEM(REG_DEVT2, _BIT3, _MASK1),
|
||||
DEVT2_JIG_UART_ON = REG_ITEM(REG_DEVT2, _BIT2, _MASK1),
|
||||
DEVT2_JIG_USB_OFF = REG_ITEM(REG_DEVT2, _BIT1, _MASK1),
|
||||
DEVT2_JIG_USB_ON = REG_ITEM(REG_DEVT2, _BIT0, _MASK1),
|
||||
|
||||
MANSW1_DM_CON_SW = REG_ITEM(REG_MANSW1, _BIT5, _MASK3),
|
||||
MANSW1_DP_CON_SW = REG_ITEM(REG_MANSW1, _BIT2, _MASK3),
|
||||
|
||||
MANSW2_BOOT_SW = REG_ITEM(REG_MANSW2, _BIT3, _MASK1),
|
||||
MANSW2_JIG_ON = REG_ITEM(REG_MANSW2, _BIT2, _MASK1),
|
||||
MANSW2_VBUS = REG_ITEM(REG_MANSW2, _BIT0, _MASK1),
|
||||
|
||||
RESET_RESET = REG_ITEM(REG_RESET, _BIT0, _MASK1),
|
||||
|
||||
CHGTYPE_CHG_TYPE = REG_ITEM(REG_CHGTYPE, _BIT0, _MASK5),
|
||||
};
|
||||
|
||||
enum sm5504_muic_devt1{
|
||||
SM5504_DEVT1_DCP = (0x1 << 6),
|
||||
SM5504_DEVT1_CDP = (0x1 << 5),
|
||||
SM5504_DEVT1_CAR_KIT_CHARGER = (0x1 << 4),
|
||||
SM5504_DEVT1_UART = (0x1 << 3),
|
||||
SM5504_DEVT1_USB = (0x1 << 2),
|
||||
SM5504_DEVT1_USB_OTG = (0x1 << 0),
|
||||
};
|
||||
|
||||
enum sm5504_muic_devt2{
|
||||
SM5504_DEVT2_UNKOWN = (0x1 << 6),
|
||||
SM5504_DEVT2_JIG_UART_OFF = (0x1 << 3),
|
||||
SM5504_DEVT2_JIG_UART_ON = (0x1 << 2),
|
||||
SM5504_DEVT2_JIG_USB_OFF = (0x1 << 1),
|
||||
SM5504_DEVT2_JIG_USB_ON = (0x1 << 0),
|
||||
};
|
||||
|
||||
/* sm5504 Control register */
|
||||
#define CTRL_ADC_EN_SHIFT 7
|
||||
#define CTRL_USBCHDEN_SHIFT 6
|
||||
#define CTRL_CHGTYP_SHIFT 5
|
||||
#define CTRL_SWITCH_OPEN_SHIFT 4
|
||||
#define CTRL_RAW_DATA_SHIFT 3
|
||||
#define CTRL_MANUAL_SW_SHIFT 2
|
||||
#define CTRL_INT_MASK_SHIFT 0
|
||||
|
||||
#define CTRL_ADC_EN_MASK (0x1 << CTRL_ADC_EN_SHIFT)
|
||||
#define CTRL_USBCHDEN_MASK (0x1 << CTRL_USBCHDEN_SHIFT)
|
||||
#define CTRL_CHGTYP_MASK (0x1 << CTRL_CHGTYP_SHIFT)
|
||||
#define CTRL_SWITCH_OPEN_MASK (0x0 << CTRL_SWITCH_OPEN_SHIFT)
|
||||
#define CTRL_RAW_DATA_MASK (0x0 << CTRL_RAW_DATA_SHIFT)
|
||||
#define CTRL_MANUAL_SW_MASK (0x1 << CTRL_MANUAL_SW_SHIFT)
|
||||
#define CTRL_INT_MASK_MASK (0x1 << CTRL_INT_MASK_SHIFT)
|
||||
#define CTRL_MASK (CTRL_ADC_EN_MASK | CTRL_USBCHDEN_MASK | \
|
||||
CTRL_CHGTYP_MASK | CTRL_SWITCH_OPEN_MASK | \
|
||||
CTRL_RAW_DATA_MASK | CTRL_MANUAL_SW_MASK | \
|
||||
CTRL_INT_MASK_MASK)
|
||||
|
||||
#define INT2_UVLO_MASK (0x1 << 1)
|
||||
#define REG_CTRL_INITIAL (CTRL_MASK | CTRL_MANUAL_SW_MASK)
|
||||
|
||||
/*
|
||||
* Manual Switch
|
||||
* D- [7:5] / D+ [4:2] / Vbus [1:0]
|
||||
* 000: Open all / 001: USB / 010: AUDIO / 011: UART / 100: V_AUDIO
|
||||
* 00: Vbus to Open / 01: Vbus to Charger / 10: Vbus to MIC / 11: Vbus to VBout
|
||||
*/
|
||||
#define _D_OPEN (0x0)
|
||||
#define _D_USB (0x1)
|
||||
#define _D_UART (0x3)
|
||||
|
||||
/* COM patch Values */
|
||||
#define COM_VALUE(dm) ((dm<<5) | (dm<<2))
|
||||
|
||||
#define _COM_OPEN COM_VALUE(_D_OPEN)
|
||||
#define _COM_UART_AP COM_VALUE(_D_UART)
|
||||
#define _COM_UART_CP _COM_UART_AP
|
||||
#define _COM_USB_AP COM_VALUE(_D_USB)
|
||||
#define _COM_USB_CP _COM_USB_AP
|
||||
|
||||
static int sm5703_com_value_tbl[] = {
|
||||
[COM_OPEN] = _COM_OPEN,
|
||||
[COM_UART_AP] = _COM_UART_AP,
|
||||
[COM_UART_CP] = _COM_UART_CP,
|
||||
[COM_USB_AP] = _COM_USB_AP,
|
||||
[COM_USB_CP] = _COM_USB_CP,
|
||||
};
|
||||
|
||||
#define INIT_VBUS_140MS (0x0E)
|
||||
#define INIT_VBUS_300MS (0x06)
|
||||
|
||||
static regmap_t sm5504_muic_regmap_table[] = {
|
||||
[REG_DEVID] = {"DeviceID", 0x01, 0x00, INIT_NONE},
|
||||
[REG_CTRL] = {"CONTROL", 0x1F, 0x00, REG_CTRL_INITIAL,},
|
||||
[REG_INT1] = {"INT1", 0x00, 0x00, INIT_NONE,},
|
||||
[REG_INT2] = {"INT2", 0x00, 0x00, INIT_NONE,},
|
||||
[REG_INTMASK1] = {"INTMASK1", 0x00, 0x00, REG_INTMASK1_VALUE,},
|
||||
[REG_INTMASK2] = {"INTMASK2", 0x00, 0x00, REG_INTMASK2_VALUE,},
|
||||
[REG_ADC] = {"ADC", 0x1F, 0x00, INIT_NONE,},
|
||||
[REG_DEVT1] = {"DEVICETYPE1", 0xFF, 0x00, INIT_NONE,},
|
||||
[REG_DEVT2] = {"DEVICETYPE2", 0xFF, 0x00, INIT_NONE,},
|
||||
/* 0x0F ~ 0x12: Reserved */
|
||||
[REG_MANSW1] = {"ManualSW1", 0x00, 0x00, INIT_NONE,},
|
||||
[REG_MANSW2] = {"ManualSW2", 0x00, 0x00, INIT_NONE,},
|
||||
/* 0x16 ~ 0x1A: Reserved */
|
||||
[REG_RESET] = {"RESET", 0x00, 0x00, INIT_NONE,},
|
||||
/* 0x1C: Reserved */
|
||||
/* 0x1E ~ 0x1F: Reserved */
|
||||
[REG_SET] = {"Reserved_ID2", 0x06, 0x00, INIT_VBUS_140MS,},
|
||||
/* 0x22: Reserved */
|
||||
[REG_CHGTYPE] = {"REG_CHG_TYPE", 0xFF, 0x00, INIT_NONE,},
|
||||
[REG_END] = {NULL, 0, 0, INIT_NONE},
|
||||
};
|
||||
|
||||
static int sm5504_muic_ioctl(struct regmap_desc *pdesc,
|
||||
int arg1, int *arg2, int *arg3)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (arg1) {
|
||||
case GET_COM_VAL:
|
||||
*arg2 = sm5703_com_value_tbl[*arg2];
|
||||
*arg3 = REG_MANSW1;
|
||||
break;
|
||||
case GET_CTLREG:
|
||||
*arg3 = REG_CTRL;
|
||||
break;
|
||||
|
||||
case GET_ADC:
|
||||
*arg3 = ADC_VALUE;
|
||||
break;
|
||||
|
||||
case GET_SWITCHING_MODE:
|
||||
*arg3 = CTRL_ManualSW;
|
||||
break;
|
||||
|
||||
case GET_INT_MASK:
|
||||
*arg3 = CTRL_MASK_INT;
|
||||
break;
|
||||
|
||||
case GET_REVISION:
|
||||
*arg3 = DEVID_VendorID;
|
||||
break;
|
||||
|
||||
case GET_OTG_STATUS:
|
||||
*arg3 = INTMASK2_RID_CHG;
|
||||
break;
|
||||
|
||||
case GET_CHGTYPE:
|
||||
*arg3 = CHGTYPE_CHG_TYPE;
|
||||
break;
|
||||
|
||||
case GET_RESID3:
|
||||
*arg3 = CTRL_USBCHDEN;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pdesc->trace) {
|
||||
pr_info("%s: ret:%d arg1:%x,", __func__, ret, arg1);
|
||||
|
||||
if (arg2)
|
||||
pr_info(" arg2:%x,", *arg2);
|
||||
|
||||
if (arg3)
|
||||
pr_info(" arg3:%x - %s", *arg3,
|
||||
regmap_to_name(pdesc, _ATTR_ADDR(*arg3)));
|
||||
pr_info("\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sm5504_attach_ta(struct regmap_desc *pdesc)
|
||||
{
|
||||
int attr = 0, value = 0, ret = 0;
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
do {
|
||||
attr = REG_MANSW1 | _ATTR_OVERWRITE_M;
|
||||
value = _COM_OPEN;
|
||||
ret = regmap_write_value(pdesc, attr, value);
|
||||
if (ret < 0) {
|
||||
pr_err("%s REG_MANSW1 write fail.\n", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
_REGMAP_TRACE(pdesc, 'w', ret, attr, value);
|
||||
|
||||
attr = CTRL_ManualSW;
|
||||
value = 0; /* manual switching mode */
|
||||
ret = regmap_write_value(pdesc, attr, value);
|
||||
if (ret < 0) {
|
||||
pr_err("%s REG_CTRL write fail.\n", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
_REGMAP_TRACE(pdesc, 'w', ret, attr, value);
|
||||
} while (0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sm5504_detach_ta(struct regmap_desc *pdesc)
|
||||
{
|
||||
int attr = 0, value = 0, ret = 0;
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
do {
|
||||
attr = REG_MANSW1 | _ATTR_OVERWRITE_M;
|
||||
value = _COM_OPEN;
|
||||
ret = regmap_write_value(pdesc, attr, value);
|
||||
if (ret < 0) {
|
||||
pr_err("%s REG_MANSW1 write fail.\n", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
_REGMAP_TRACE(pdesc, 'w', ret, attr, value);
|
||||
|
||||
attr = CTRL_ManualSW;
|
||||
value = 1; /* auto switching mode */
|
||||
ret = regmap_write_value(pdesc, attr, value);
|
||||
if (ret < 0) {
|
||||
pr_err("%s REG_CTRL write fail.\n", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
_REGMAP_TRACE(pdesc, 'w', ret, attr, value);
|
||||
|
||||
} while (0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sm5504_set_rustproof(struct regmap_desc *pdesc, int op)
|
||||
{
|
||||
int attr = 0, value = 0, ret = 0;
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
do {
|
||||
attr = MANSW2_JIG_ON;
|
||||
value = op ? 1 : 0;
|
||||
ret = regmap_write_value(pdesc, attr, value);
|
||||
if (ret < 0) {
|
||||
pr_err("%s MANSW2_JIG_ON write fail.\n", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
_REGMAP_TRACE(pdesc, 'w', ret, attr, value);
|
||||
|
||||
attr = CTRL_ManualSW;
|
||||
value = op ? 0 : 1;
|
||||
ret = regmap_write_value(pdesc, attr, value);
|
||||
if (ret < 0) {
|
||||
pr_err("%s CTRL_ManualSW write fail.\n", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
_REGMAP_TRACE(pdesc, 'w', ret, attr, value);
|
||||
|
||||
} while (0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sm5504_set_manual_JIGON(struct regmap_desc *pdesc, int jig_attached)
|
||||
{
|
||||
int attr = 0, value = 0, ret = 0;
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
do {
|
||||
attr = MANSW2_JIG_ON;
|
||||
value = jig_attached ? 1 : 0; /* 1: LOW, 0: High impedance */
|
||||
ret = regmap_write_value(pdesc, attr, value);
|
||||
if (ret < 0) {
|
||||
pr_err("%s MANSW2_JIG_ON write fail.\n", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
_REGMAP_TRACE(pdesc, 'w', ret, attr, value);
|
||||
|
||||
attr = CTRL_ManualSW;
|
||||
value = jig_attached ? 0 : 1; /* 0: manual, 1: auto */
|
||||
ret = regmap_write_value(pdesc, attr, value);
|
||||
if (ret < 0) {
|
||||
pr_err("%s CTRL_ManualSW write fail.\n", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
_REGMAP_TRACE(pdesc, 'w', ret, attr, value);
|
||||
|
||||
} while (0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sm5504_get_vps_data(struct regmap_desc *pdesc, void *pbuf)
|
||||
{
|
||||
muic_data_t *pmuic = pdesc->muic;
|
||||
vps_data_t *pvps = (vps_data_t *)pbuf;
|
||||
int attr;
|
||||
|
||||
if (pdesc->trace)
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
*(u8 *)&pvps->s.val1 = muic_i2c_read_byte(pmuic->i2c, REG_DEVT1);
|
||||
*(u8 *)&pvps->s.val2 = muic_i2c_read_byte(pmuic->i2c, REG_DEVT2);
|
||||
|
||||
attr = ADC_VALUE;
|
||||
*(u8 *)&pvps->s.adc = regmap_read_value(pdesc, attr);
|
||||
|
||||
pvps->t.attached_dev = ATTACHED_DEV_UNKNOWN_MUIC;
|
||||
pvps->s.vbvolt = !(pmuic->intr.intr2 & INT2_UVLO_MASK);
|
||||
|
||||
switch (pvps->s.val1) {
|
||||
case SM5504_DEVT1_CDP:
|
||||
pvps->t.attached_dev = ATTACHED_DEV_CDP_MUIC;
|
||||
pr_info("%s : USB_CDP DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
case SM5504_DEVT1_USB:
|
||||
pvps->t.attached_dev = ATTACHED_DEV_USB_MUIC;
|
||||
pr_info("%s : USB DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
case SM5504_DEVT1_CAR_KIT_CHARGER:
|
||||
case SM5504_DEVT1_DCP:
|
||||
if (ADC_CEA936ATYPE1_CHG != pvps->s.adc) {
|
||||
pvps->t.attached_dev = ATTACHED_DEV_TA_MUIC;
|
||||
pr_info("%s : DEDICATED CHARGER DETECTED\n", MUIC_DEV_NAME);
|
||||
}
|
||||
break;
|
||||
case SM5504_DEVT1_USB_OTG:
|
||||
pvps->t.attached_dev = ATTACHED_DEV_OTG_MUIC;
|
||||
pr_info("%s : USB_OTG DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (pvps->s.val2) {
|
||||
case SM5504_DEVT2_JIG_UART_OFF:
|
||||
pvps->t.attached_dev = ATTACHED_DEV_JIG_UART_OFF_MUIC;
|
||||
pr_info("%s : JIG_UART_OFF DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
case SM5504_DEVT2_JIG_USB_OFF:
|
||||
pvps->t.attached_dev = ATTACHED_DEV_JIG_USB_OFF_MUIC;
|
||||
pr_info("%s : JIG_USB_OFF DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
case SM5504_DEVT2_JIG_USB_ON:
|
||||
pvps->t.attached_dev = ATTACHED_DEV_JIG_USB_ON_MUIC;
|
||||
pr_info("%s : JIG_USB_ON DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
case SM5504_DEVT2_JIG_UART_ON:
|
||||
pvps->t.attached_dev = ATTACHED_DEV_JIG_UART_ON_MUIC;
|
||||
pr_info("%s : JIG_UART_ON DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum switching_mode_val{
|
||||
_SWMODE_AUTO =1,
|
||||
_SWMODE_MANUAL =0,
|
||||
};
|
||||
|
||||
static int sm5504_get_switching_mode(struct regmap_desc *pdesc)
|
||||
{
|
||||
int attr, value, mode;
|
||||
|
||||
pr_info("%s\n",__func__);
|
||||
|
||||
attr = CTRL_ManualSW;
|
||||
value = regmap_read_value(pdesc, attr);
|
||||
|
||||
mode = (value == _SWMODE_AUTO) ? SWMODE_AUTO : SWMODE_MANUAL;
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
static void sm5504_set_switching_mode(struct regmap_desc *pdesc, int mode)
|
||||
{
|
||||
int attr, value;
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s\n",__func__);
|
||||
|
||||
value = (mode == SWMODE_AUTO) ? _SWMODE_AUTO : _SWMODE_MANUAL;
|
||||
attr = CTRL_ManualSW;
|
||||
ret = regmap_write_value(pdesc, attr, value);
|
||||
if (ret < 0)
|
||||
pr_err("%s REG_CTRL write fail.\n", __func__);
|
||||
else
|
||||
_REGMAP_TRACE(pdesc, 'w', ret, attr, value);
|
||||
}
|
||||
|
||||
static void sm5504_get_fromatted_dump(struct regmap_desc *pdesc, char *mesg)
|
||||
{
|
||||
muic_data_t *muic = pdesc->muic;
|
||||
int val;
|
||||
|
||||
if (pdesc->trace)
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
val = i2c_smbus_read_byte_data(muic->i2c, REG_CTRL);
|
||||
sprintf(mesg, "CT:%x ", val);
|
||||
val = i2c_smbus_read_byte_data(muic->i2c, REG_INTMASK1);
|
||||
sprintf(mesg+strlen(mesg), "IM1:%x ", val);
|
||||
val = i2c_smbus_read_byte_data(muic->i2c, REG_INTMASK2);
|
||||
sprintf(mesg+strlen(mesg), "IM2:%x ", val);
|
||||
val = i2c_smbus_read_byte_data(muic->i2c, REG_MANSW1);
|
||||
sprintf(mesg+strlen(mesg), "MS1:%x ", val);
|
||||
val = i2c_smbus_read_byte_data(muic->i2c, REG_MANSW2);
|
||||
sprintf(mesg+strlen(mesg), "MS2:%x ", val);
|
||||
val = i2c_smbus_read_byte_data(muic->i2c, REG_ADC);
|
||||
sprintf(mesg+strlen(mesg), "ADC:%x ", val);
|
||||
val = i2c_smbus_read_byte_data(muic->i2c, REG_DEVT1);
|
||||
sprintf(mesg+strlen(mesg), "DT1:%x ", val);
|
||||
val = i2c_smbus_read_byte_data(muic->i2c, REG_DEVT2);
|
||||
sprintf(mesg+strlen(mesg), "DT2:%x ", val);
|
||||
val = i2c_smbus_read_byte_data(muic->i2c, REG_SET);
|
||||
sprintf(mesg+strlen(mesg), "RS2:%x", val);
|
||||
}
|
||||
|
||||
static int sm5504_get_sizeof_regmap(void)
|
||||
{
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
return (int)ARRAY_SIZE(sm5504_muic_regmap_table);
|
||||
}
|
||||
|
||||
static struct regmap_ops sm5504_muic_regmap_ops = {
|
||||
.get_size = sm5504_get_sizeof_regmap,
|
||||
.ioctl = sm5504_muic_ioctl,
|
||||
.get_formatted_dump = sm5504_get_fromatted_dump,
|
||||
};
|
||||
|
||||
static struct vendor_ops sm5504_muic_vendor_ops = {
|
||||
.attach_ta = sm5504_attach_ta,
|
||||
.detach_ta = sm5504_detach_ta,
|
||||
.get_switch = sm5504_get_switching_mode,
|
||||
.set_switch = sm5504_set_switching_mode,
|
||||
.get_vps_data = sm5504_get_vps_data,
|
||||
.set_rustproof = sm5504_set_rustproof,
|
||||
.set_manual_JIGON = sm5504_set_manual_JIGON,
|
||||
};
|
||||
|
||||
static struct regmap_desc sm5504_muic_regmap_desc = {
|
||||
.name = "sm5504-MUIC",
|
||||
.regmap = sm5504_muic_regmap_table,
|
||||
.size = REG_END,
|
||||
.regmapops = &sm5504_muic_regmap_ops,
|
||||
.vendorops = &sm5504_muic_vendor_ops,
|
||||
};
|
||||
|
||||
void muic_register_sm5504_regmap_desc(struct regmap_desc **pdesc)
|
||||
{
|
||||
*pdesc = &sm5504_muic_regmap_desc;
|
||||
}
|
377
drivers/muic/universal/muic_state.c
Normal file
377
drivers/muic/universal/muic_state.c
Normal file
|
@ -0,0 +1,377 @@
|
|||
/*
|
||||
* muic_state.c
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics
|
||||
* Thomas Ryu <smilesr.ryu@samsung.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/host_notify.h>
|
||||
|
||||
#include <linux/muic/muic.h>
|
||||
|
||||
#if defined(CONFIG_MUIC_NOTIFIER)
|
||||
#include <linux/muic/muic_notifier.h>
|
||||
#endif /* CONFIG_MUIC_NOTIFIER */
|
||||
|
||||
#if defined(CONFIG_VBUS_NOTIFIER)
|
||||
#include <linux/vbus_notifier.h>
|
||||
#endif /* CONFIG_VBUS_NOTIFIER */
|
||||
|
||||
#if defined (CONFIG_OF)
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
#include "muic-internal.h"
|
||||
#include "muic_apis.h"
|
||||
#include "muic_i2c.h"
|
||||
#include "muic_vps.h"
|
||||
#include "muic_regmap.h"
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_SM5705)
|
||||
#include <linux/muic/muic_afc.h>
|
||||
#endif
|
||||
|
||||
extern int muic_wakeup_noti;
|
||||
|
||||
void muic_set_wakeup_noti(int flag)
|
||||
{
|
||||
pr_info("%s: %d\n", __func__, flag);
|
||||
muic_wakeup_noti = flag;
|
||||
}
|
||||
|
||||
static void muic_handle_attach(muic_data_t *pmuic,
|
||||
muic_attached_dev_t new_dev, int adc, u8 vbvolt)
|
||||
{
|
||||
int ret = 0;
|
||||
bool noti_f = true;
|
||||
|
||||
pr_info("%s:%s attached_dev:%d new_dev:%d adc:0x%02x, vbvolt:%02x\n",
|
||||
MUIC_DEV_NAME, __func__, pmuic->attached_dev, new_dev, adc, vbvolt);
|
||||
|
||||
if((new_dev == pmuic->attached_dev) &&
|
||||
(new_dev != ATTACHED_DEV_JIG_UART_OFF_MUIC)) {
|
||||
pr_info("%s:%s Duplicated device %d just ignore\n",
|
||||
MUIC_DEV_NAME, __func__,pmuic->attached_dev);
|
||||
return;
|
||||
}
|
||||
switch (pmuic->attached_dev) {
|
||||
case ATTACHED_DEV_USB_MUIC:
|
||||
case ATTACHED_DEV_CDP_MUIC:
|
||||
case ATTACHED_DEV_JIG_USB_OFF_MUIC:
|
||||
case ATTACHED_DEV_JIG_USB_ON_MUIC:
|
||||
if (new_dev != pmuic->attached_dev) {
|
||||
pr_warn("%s:%s new(%d)!=attached(%d), assume detach\n",
|
||||
MUIC_DEV_NAME, __func__, new_dev,
|
||||
pmuic->attached_dev);
|
||||
ret = detach_usb(pmuic);
|
||||
}
|
||||
break;
|
||||
case ATTACHED_DEV_HMT_MUIC:
|
||||
case ATTACHED_DEV_OTG_MUIC:
|
||||
/* OTG -> LANHUB, meaning TA is attached to LANHUB(OTG) */
|
||||
if (new_dev == ATTACHED_DEV_USB_LANHUB_MUIC) {
|
||||
pr_info("%s:%s OTG+TA=>LANHUB. Do not detach OTG.\n",
|
||||
__func__, MUIC_DEV_NAME);
|
||||
noti_f = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (new_dev == pmuic->attached_dev) {
|
||||
noti_f = false;
|
||||
break;
|
||||
}
|
||||
case ATTACHED_DEV_USB_LANHUB_MUIC:
|
||||
if (new_dev != pmuic->attached_dev) {
|
||||
pr_warn("%s:%s new(%d)!=attached(%d), assume detach\n",
|
||||
MUIC_DEV_NAME, __func__, new_dev,
|
||||
pmuic->attached_dev);
|
||||
ret = detach_otg_usb(pmuic);
|
||||
}
|
||||
break;
|
||||
|
||||
case ATTACHED_DEV_AUDIODOCK_MUIC:
|
||||
if (new_dev != pmuic->attached_dev) {
|
||||
pr_warn("%s:%s new(%d)!=attached(%d), assume detach\n",
|
||||
MUIC_DEV_NAME, __func__, new_dev,
|
||||
pmuic->attached_dev);
|
||||
ret = detach_audiodock(pmuic);
|
||||
}
|
||||
break;
|
||||
|
||||
case ATTACHED_DEV_TA_MUIC:
|
||||
pmuic->attached_dev = ATTACHED_DEV_NONE_MUIC;
|
||||
break;
|
||||
case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC:
|
||||
case ATTACHED_DEV_JIG_UART_OFF_MUIC:
|
||||
if (new_dev != ATTACHED_DEV_JIG_UART_OFF_MUIC) {
|
||||
pr_warn("%s:%s new(%d)!=attached(%d), assume detach\n",
|
||||
MUIC_DEV_NAME, __func__, new_dev,
|
||||
pmuic->attached_dev);
|
||||
ret = detach_jig_uart_boot_off(pmuic);
|
||||
}
|
||||
break;
|
||||
|
||||
case ATTACHED_DEV_JIG_UART_ON_MUIC:
|
||||
if (new_dev != pmuic->attached_dev) {
|
||||
pr_warn("%s:%s new(%d)!=attached(%d), assume detach\n",
|
||||
MUIC_DEV_NAME, __func__, new_dev,
|
||||
pmuic->attached_dev);
|
||||
|
||||
if (pmuic->is_factory_start)
|
||||
ret = detach_deskdock(pmuic);
|
||||
else
|
||||
ret = detach_jig_uart_boot_on(pmuic);
|
||||
|
||||
muic_set_wakeup_noti(pmuic->is_factory_start ? 1: 0);
|
||||
}
|
||||
break;
|
||||
case ATTACHED_DEV_DESKDOCK_MUIC:
|
||||
case ATTACHED_DEV_DESKDOCK_VB_MUIC:
|
||||
if (new_dev == ATTACHED_DEV_DESKDOCK_MUIC ||
|
||||
new_dev == ATTACHED_DEV_DESKDOCK_VB_MUIC) {
|
||||
pr_warn("%s:%s new(%d)!=attached(%d), assume same device\n",
|
||||
MUIC_DEV_NAME, __func__, new_dev,
|
||||
pmuic->attached_dev);
|
||||
noti_f = false;
|
||||
} else if (new_dev != pmuic->attached_dev) {
|
||||
pr_warn("%s:%s new(%d)!=attached(%d), assume detach\n",
|
||||
MUIC_DEV_NAME, __func__, new_dev,
|
||||
pmuic->attached_dev);
|
||||
ret = detach_deskdock(pmuic);
|
||||
}
|
||||
break;
|
||||
case ATTACHED_DEV_UNIVERSAL_MMDOCK_MUIC:
|
||||
if (new_dev != pmuic->attached_dev) {
|
||||
pr_warn("%s:%s new(%d)!=attached(%d), assume detach\n",
|
||||
MUIC_DEV_NAME, __func__, new_dev,
|
||||
pmuic->attached_dev);
|
||||
ret = detach_otg_usb(pmuic);
|
||||
}
|
||||
break;
|
||||
case ATTACHED_DEV_CHARGING_CABLE_MUIC:
|
||||
if (new_dev != pmuic->attached_dev) {
|
||||
pr_warn("%s:%s new(%d)!=attached(%d), assume detach\n",
|
||||
MUIC_DEV_NAME, __func__, new_dev,
|
||||
pmuic->attached_dev);
|
||||
ret = detach_ps_cable(pmuic);
|
||||
}
|
||||
break;
|
||||
case ATTACHED_DEV_UNDEFINED_CHARGING_MUIC:
|
||||
break;
|
||||
|
||||
default:
|
||||
noti_f = false;
|
||||
}
|
||||
|
||||
if (noti_f)
|
||||
muic_notifier_detach_attached_dev(pmuic->attached_dev);
|
||||
|
||||
noti_f = true;
|
||||
switch (new_dev) {
|
||||
case ATTACHED_DEV_USB_MUIC:
|
||||
case ATTACHED_DEV_CDP_MUIC:
|
||||
ret = attach_usb(pmuic, new_dev);
|
||||
break;
|
||||
case ATTACHED_DEV_HMT_MUIC:
|
||||
case ATTACHED_DEV_OTG_MUIC:
|
||||
case ATTACHED_DEV_USB_LANHUB_MUIC:
|
||||
ret = attach_otg_usb(pmuic, new_dev);
|
||||
break;
|
||||
case ATTACHED_DEV_AUDIODOCK_MUIC:
|
||||
ret = attach_audiodock(pmuic, new_dev, vbvolt);
|
||||
break;
|
||||
case ATTACHED_DEV_TA_MUIC:
|
||||
attach_ta(pmuic);
|
||||
pmuic->attached_dev = new_dev;
|
||||
break;
|
||||
case ATTACHED_DEV_JIG_UART_OFF_MUIC:
|
||||
new_dev = attach_jig_uart_boot_off(pmuic, new_dev, vbvolt);
|
||||
break;
|
||||
case ATTACHED_DEV_JIG_UART_ON_MUIC:
|
||||
/* Keep AP UART path and
|
||||
* call attach_deskdock to wake up the device in the Facory Build Binary.
|
||||
*/
|
||||
if (pmuic->is_factory_start)
|
||||
ret = attach_deskdock(pmuic, new_dev);
|
||||
else
|
||||
ret = attach_jig_uart_boot_on(pmuic, new_dev);
|
||||
|
||||
muic_set_wakeup_noti(pmuic->is_factory_start ? 1: 0);
|
||||
break;
|
||||
case ATTACHED_DEV_JIG_USB_OFF_MUIC:
|
||||
ret = attach_jig_usb_boot_off(pmuic, vbvolt);
|
||||
break;
|
||||
case ATTACHED_DEV_JIG_USB_ON_MUIC:
|
||||
ret = attach_jig_usb_boot_on(pmuic, vbvolt);
|
||||
break;
|
||||
case ATTACHED_DEV_MHL_MUIC:
|
||||
ret = attach_mhl(pmuic);
|
||||
break;
|
||||
case ATTACHED_DEV_DESKDOCK_MUIC:
|
||||
case ATTACHED_DEV_DESKDOCK_VB_MUIC:
|
||||
ret = attach_deskdock(pmuic, new_dev);
|
||||
break;
|
||||
case ATTACHED_DEV_UNIVERSAL_MMDOCK_MUIC:
|
||||
ret = attach_otg_usb(pmuic, new_dev);
|
||||
break;
|
||||
case ATTACHED_DEV_CHARGING_CABLE_MUIC:
|
||||
ret = attach_ps_cable(pmuic, new_dev);
|
||||
break;
|
||||
case ATTACHED_DEV_UNDEFINED_CHARGING_MUIC:
|
||||
com_to_open_with_vbus(pmuic);
|
||||
pmuic->attached_dev = new_dev;
|
||||
break;
|
||||
case ATTACHED_DEV_VZW_INCOMPATIBLE_MUIC:
|
||||
com_to_open_with_vbus(pmuic);
|
||||
pmuic->attached_dev = new_dev;
|
||||
break;
|
||||
default:
|
||||
pr_warn("%s:%s unsupported dev=%d, adc=0x%x, vbus=%c\n",
|
||||
MUIC_DEV_NAME, __func__, new_dev, adc,
|
||||
(vbvolt ? 'O' : 'X'));
|
||||
break;
|
||||
}
|
||||
|
||||
if (noti_f)
|
||||
muic_notifier_attach_attached_dev(new_dev);
|
||||
else
|
||||
pr_info("%s:%s attach Noti. for (%d) discarded.\n",
|
||||
MUIC_DEV_NAME, __func__, new_dev);
|
||||
|
||||
if (ret < 0)
|
||||
pr_warn("%s:%s something wrong with attaching %d (ERR=%d)\n",
|
||||
MUIC_DEV_NAME, __func__, new_dev, ret);
|
||||
}
|
||||
|
||||
static void muic_handle_detach(muic_data_t *pmuic)
|
||||
{
|
||||
int ret = 0;
|
||||
bool noti_f = true;
|
||||
|
||||
ret = com_to_open_with_vbus(pmuic);
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_SM5705)
|
||||
// ENAFC set '0'
|
||||
pmuic->regmapdesc->afcops->afc_ctrl_reg(pmuic->regmapdesc, AFCCTRL_ENAFC, 0);
|
||||
#endif
|
||||
//muic_enable_accdet(pmuic);
|
||||
|
||||
switch (pmuic->attached_dev) {
|
||||
case ATTACHED_DEV_JIG_USB_OFF_MUIC:
|
||||
case ATTACHED_DEV_JIG_USB_ON_MUIC:
|
||||
case ATTACHED_DEV_USB_MUIC:
|
||||
case ATTACHED_DEV_CDP_MUIC:
|
||||
ret = detach_usb(pmuic);
|
||||
break;
|
||||
case ATTACHED_DEV_HMT_MUIC:
|
||||
case ATTACHED_DEV_OTG_MUIC:
|
||||
case ATTACHED_DEV_USB_LANHUB_MUIC:
|
||||
ret = detach_otg_usb(pmuic);
|
||||
break;
|
||||
case ATTACHED_DEV_TA_MUIC:
|
||||
pmuic->attached_dev = ATTACHED_DEV_NONE_MUIC;
|
||||
detach_ta(pmuic);
|
||||
break;
|
||||
case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC:
|
||||
case ATTACHED_DEV_JIG_UART_OFF_MUIC:
|
||||
ret = detach_jig_uart_boot_off(pmuic);
|
||||
break;
|
||||
case ATTACHED_DEV_JIG_UART_ON_MUIC:
|
||||
if (pmuic->is_factory_start)
|
||||
ret = detach_deskdock(pmuic);
|
||||
else
|
||||
ret = detach_jig_uart_boot_on(pmuic);
|
||||
|
||||
muic_set_wakeup_noti(pmuic->is_factory_start ? 1: 0);
|
||||
break;
|
||||
case ATTACHED_DEV_DESKDOCK_MUIC:
|
||||
case ATTACHED_DEV_DESKDOCK_VB_MUIC:
|
||||
ret = detach_deskdock(pmuic);
|
||||
break;
|
||||
case ATTACHED_DEV_UNIVERSAL_MMDOCK_MUIC:
|
||||
ret = detach_otg_usb(pmuic);
|
||||
break;
|
||||
case ATTACHED_DEV_AUDIODOCK_MUIC:
|
||||
ret = detach_audiodock(pmuic);
|
||||
break;
|
||||
case ATTACHED_DEV_MHL_MUIC:
|
||||
ret = detach_mhl(pmuic);
|
||||
break;
|
||||
case ATTACHED_DEV_CHARGING_CABLE_MUIC:
|
||||
ret = detach_ps_cable(pmuic);
|
||||
break;
|
||||
case ATTACHED_DEV_NONE_MUIC:
|
||||
pmuic->is_afc_device = 0;
|
||||
pr_info("%s:%s duplicated(NONE)\n", MUIC_DEV_NAME, __func__);
|
||||
break;
|
||||
case ATTACHED_DEV_UNDEFINED_CHARGING_MUIC:
|
||||
pr_info("%s:%s UNKNOWN\n", MUIC_DEV_NAME, __func__);
|
||||
pmuic->attached_dev = ATTACHED_DEV_NONE_MUIC;
|
||||
break;
|
||||
default:
|
||||
pmuic->is_afc_device = 0;
|
||||
pr_info("%s:%s invalid attached_dev type(%d)\n", MUIC_DEV_NAME,
|
||||
__func__, pmuic->attached_dev);
|
||||
pmuic->attached_dev = ATTACHED_DEV_NONE_MUIC;
|
||||
break;
|
||||
}
|
||||
|
||||
if (noti_f)
|
||||
muic_notifier_detach_attached_dev(pmuic->attached_dev);
|
||||
|
||||
else
|
||||
pr_info("%s:%s detach Noti. for (%d) discarded.\n",
|
||||
MUIC_DEV_NAME, __func__, pmuic->attached_dev);
|
||||
if (ret < 0)
|
||||
pr_warn("%s:%s something wrong with detaching %d (ERR=%d)\n",
|
||||
MUIC_DEV_NAME, __func__, pmuic->attached_dev, ret);
|
||||
|
||||
}
|
||||
|
||||
void muic_detect_dev(muic_data_t *pmuic)
|
||||
{
|
||||
muic_attached_dev_t new_dev = ATTACHED_DEV_UNKNOWN_MUIC;
|
||||
int intr = MUIC_INTR_DETACH;
|
||||
|
||||
get_vps_data(pmuic, &pmuic->vps);
|
||||
|
||||
|
||||
pr_info("%s:%s dev[1:0x%x, 2:0x%x, 3:0x%x], adc:0x%x, vbvolt:0x%x\n",
|
||||
MUIC_DEV_NAME, __func__, pmuic->vps.s.val1, pmuic->vps.s.val2,
|
||||
pmuic->vps.s.val3, pmuic->vps.s.adc, pmuic->vps.s.vbvolt);
|
||||
|
||||
|
||||
vps_resolve_dev(pmuic, &new_dev, &intr);
|
||||
|
||||
if (intr == MUIC_INTR_ATTACH) {
|
||||
muic_handle_attach(pmuic, new_dev,
|
||||
pmuic->vps.s.adc, pmuic->vps.s.vbvolt);
|
||||
} else {
|
||||
muic_handle_detach(pmuic);
|
||||
}
|
||||
#if defined(CONFIG_VBUS_NOTIFIER)
|
||||
vbus_notifier_handle(!!pmuic->vps.s.vbvolt ? STATUS_VBUS_HIGH : STATUS_VBUS_LOW);
|
||||
#endif /* CONFIG_VBUS_NOTIFIER */
|
||||
|
||||
}
|
6
drivers/muic/universal/muic_state.h
Normal file
6
drivers/muic/universal/muic_state.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#ifndef _MUIC_STATE_
|
||||
#define _MUIC_STATE_
|
||||
|
||||
extern void muic_detect_dev(muic_data_t *pmuic);
|
||||
|
||||
#endif
|
520
drivers/muic/universal/muic_sysfs.c
Normal file
520
drivers/muic/universal/muic_sysfs.c
Normal file
|
@ -0,0 +1,520 @@
|
|||
/*
|
||||
* muic_sysfs.c
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics
|
||||
* Thomas Ryu <smilesr.ryu@samsung.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/host_notify.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <linux/muic/muic.h>
|
||||
|
||||
#if defined(CONFIG_MUIC_NOTIFIER)
|
||||
#include <linux/muic/muic_notifier.h>
|
||||
#endif /* CONFIG_MUIC_NOTIFIER */
|
||||
|
||||
#if defined (CONFIG_OF)
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
#include "muic-internal.h"
|
||||
#include "muic_i2c.h"
|
||||
#include "muic_debug.h"
|
||||
#include "muic_apis.h"
|
||||
#include "muic_regmap.h"
|
||||
|
||||
static ssize_t muic_show_uart_en(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
muic_data_t *pmuic = dev_get_drvdata(dev);
|
||||
|
||||
if (!pmuic->is_rustproof) {
|
||||
pr_info("%s:%s UART ENABLE\n", MUIC_DEV_NAME, __func__);
|
||||
return sprintf(buf, "1\n");
|
||||
}
|
||||
pr_info("%s:%s UART DISABLE\n", MUIC_DEV_NAME, __func__);
|
||||
return sprintf(buf, "0\n");
|
||||
}
|
||||
|
||||
static ssize_t muic_set_uart_en(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
muic_data_t *pmuic = dev_get_drvdata(dev);
|
||||
|
||||
if (!strncmp(buf, "1", 1)) {
|
||||
pmuic->is_rustproof = false;
|
||||
} else if (!strncmp(buf, "0", 1)) {
|
||||
pmuic->is_rustproof = true;
|
||||
} else {
|
||||
pr_warn("%s:%s invalid value\n", MUIC_DEV_NAME, __func__);
|
||||
}
|
||||
|
||||
pr_info("%s:%s uart_en(%d)\n", MUIC_DEV_NAME, __func__,
|
||||
!pmuic->is_rustproof);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_UART_SEL)
|
||||
static ssize_t muic_show_uart_sel(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
muic_data_t *pmuic = dev_get_drvdata(dev);
|
||||
struct muic_platform_data *pdata = pmuic->pdata;
|
||||
|
||||
switch (pdata->uart_path) {
|
||||
case MUIC_PATH_UART_AP:
|
||||
pr_info("%s:%s AP\n", MUIC_DEV_NAME, __func__);
|
||||
return sprintf(buf, "AP\n");
|
||||
case MUIC_PATH_UART_CP:
|
||||
pr_info("%s:%s CP\n", MUIC_DEV_NAME, __func__);
|
||||
return sprintf(buf, "CP\n");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pr_info("%s:%s UNKNOWN\n", MUIC_DEV_NAME, __func__);
|
||||
return sprintf(buf, "UNKNOWN\n");
|
||||
}
|
||||
|
||||
static ssize_t muic_set_uart_sel(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
muic_data_t *pmuic = dev_get_drvdata(dev);
|
||||
struct muic_platform_data *pdata = pmuic->pdata;
|
||||
|
||||
if (!strncasecmp(buf, "AP", 2)) {
|
||||
pdata->uart_path = MUIC_PATH_UART_AP;
|
||||
switch_to_ap_uart(pmuic);
|
||||
} else if (!strncasecmp(buf, "CP", 2)) {
|
||||
pdata->uart_path = MUIC_PATH_UART_CP;
|
||||
switch_to_cp_uart(pmuic);
|
||||
} else {
|
||||
pr_warn("%s:%s invalid value\n", MUIC_DEV_NAME, __func__);
|
||||
}
|
||||
|
||||
pr_info("%s:%s uart_path(%d)\n", MUIC_DEV_NAME, __func__,
|
||||
pdata->uart_path);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t muic_show_usb_sel(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
muic_data_t *pmuic = dev_get_drvdata(dev);
|
||||
struct muic_platform_data *pdata = pmuic->pdata;
|
||||
|
||||
switch (pdata->usb_path) {
|
||||
case MUIC_PATH_USB_AP:
|
||||
return sprintf(buf, "PDA\n");
|
||||
case MUIC_PATH_USB_CP:
|
||||
return sprintf(buf, "MODEM\n");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pr_info("%s:%s UNKNOWN\n", MUIC_DEV_NAME, __func__);
|
||||
return sprintf(buf, "UNKNOWN\n");
|
||||
}
|
||||
|
||||
static ssize_t muic_set_usb_sel(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
muic_data_t *pmuic = dev_get_drvdata(dev);
|
||||
struct muic_platform_data *pdata = pmuic->pdata;
|
||||
|
||||
if (!strncasecmp(buf, "PDA", 3)) {
|
||||
pdata->usb_path = MUIC_PATH_USB_AP;
|
||||
} else if (!strncasecmp(buf, "MODEM", 5)) {
|
||||
pdata->usb_path = MUIC_PATH_USB_CP;
|
||||
} else {
|
||||
pr_warn("%s:%s invalid value\n", MUIC_DEV_NAME, __func__);
|
||||
}
|
||||
|
||||
pr_info("%s:%s usb_path(%d)\n", MUIC_DEV_NAME, __func__,
|
||||
pdata->usb_path);
|
||||
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
static ssize_t muic_show_adc(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
muic_data_t *pmuic = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&pmuic->muic_mutex);
|
||||
ret = get_adc(pmuic);
|
||||
mutex_unlock(&pmuic->muic_mutex);
|
||||
if (ret < 0) {
|
||||
pr_err("%s:%s err read adc reg(%d)\n", MUIC_DEV_NAME, __func__,
|
||||
ret);
|
||||
return sprintf(buf, "UNKNOWN\n");
|
||||
}
|
||||
|
||||
return sprintf(buf, "%x\n", ret);
|
||||
}
|
||||
|
||||
static ssize_t muic_show_usb_state(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
muic_data_t *pmuic = dev_get_drvdata(dev);
|
||||
|
||||
switch (pmuic->attached_dev) {
|
||||
case ATTACHED_DEV_USB_MUIC:
|
||||
case ATTACHED_DEV_CDP_MUIC:
|
||||
case ATTACHED_DEV_JIG_USB_OFF_MUIC:
|
||||
case ATTACHED_DEV_JIG_USB_ON_MUIC:
|
||||
return sprintf(buf, "USB_STATE_CONFIGURED\n");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_MUIC
|
||||
static ssize_t muic_show_registers(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
muic_data_t *pmuic = dev_get_drvdata(dev);
|
||||
char mesg[256] = "";
|
||||
|
||||
mutex_lock(&pmuic->muic_mutex);
|
||||
muic_read_reg_dump(pmuic, mesg);
|
||||
mutex_unlock(&pmuic->muic_mutex);
|
||||
pr_info("%s:%s\n", __func__, mesg);
|
||||
|
||||
return sprintf(buf, "%s\n", mesg);
|
||||
}
|
||||
|
||||
static char reg_dump_buf[256];
|
||||
static ssize_t muic_show_reg_sel(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
pr_info("%s:%s\n", __func__, reg_dump_buf);
|
||||
|
||||
return sprintf(buf, "%s\n", reg_dump_buf);
|
||||
}
|
||||
|
||||
static ssize_t muic_set_reg_sel(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
muic_data_t *pmuic = dev_get_drvdata(dev);
|
||||
int len = strlen(buf);
|
||||
unsigned int reg_base = 0, reg_num = 0;
|
||||
int ret = -EINVAL, i = 0;
|
||||
|
||||
#if 0 /* For the compatibility in 64 bit */
|
||||
pr_info("%s:%s -> %s(%d, %d)\n", MUIC_DEV_NAME, __func__,
|
||||
buf, count, len);
|
||||
#endif
|
||||
if (len < 6) {
|
||||
ret = kstrtoint(buf, 0, ®_base);
|
||||
if (ret) {
|
||||
pr_err("%s: Undefined Regs\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
reg_num = 1;
|
||||
} else if (len < 10) {
|
||||
char *ptr;
|
||||
char reg_buf[8];
|
||||
|
||||
strcpy(reg_buf, buf);
|
||||
ptr = strstr(reg_buf, "++");
|
||||
*ptr = 0x00;
|
||||
ret = kstrtoint(reg_buf, 0, ®_base);
|
||||
if (ret) {
|
||||
pr_err("%s: Undefined Regs\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
ret = kstrtoint(ptr + 2, 0, ®_num);
|
||||
if (ret) {
|
||||
pr_err("%s: Undefined Regs\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
pr_err("%s: Undefined Regs\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
pr_info(" (reg_base,reg_num) = (0x%02x,%d)\n", reg_base, reg_num);
|
||||
|
||||
memset(reg_dump_buf, 0x00, sizeof(reg_dump_buf));
|
||||
|
||||
while (reg_num--) {
|
||||
mutex_lock(&pmuic->muic_mutex);
|
||||
ret = muic_i2c_read_byte(pmuic->i2c, reg_base + i);
|
||||
mutex_unlock(&pmuic->muic_mutex);
|
||||
if (ret < 0) {
|
||||
pr_err("%s:%s err read %d\n", MUIC_DEV_NAME, __func__,
|
||||
reg_base);
|
||||
goto err;
|
||||
}
|
||||
pr_info(" [%02x] : %02x\n", reg_base, ret);
|
||||
|
||||
sprintf(reg_dump_buf + strlen(reg_dump_buf),
|
||||
" [%02x] : %02x\n", reg_base + i++, ret);
|
||||
}
|
||||
|
||||
err:
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_USB_HOST_NOTIFY)
|
||||
static ssize_t muic_show_otg_test(struct device *dev,
|
||||
struct device_attribute *pattr,
|
||||
char *buf)
|
||||
{
|
||||
muic_data_t *pmuic = dev_get_drvdata(dev);
|
||||
struct regmap_ops *pops = pmuic->regmapdesc->regmapops;
|
||||
int uattr;
|
||||
u8 val = 0;
|
||||
|
||||
mutex_lock(&pmuic->muic_mutex);
|
||||
pops->ioctl(pmuic->regmapdesc, GET_OTG_STATUS, NULL, &uattr);
|
||||
val = regmap_read_value(pmuic->regmapdesc, uattr);
|
||||
mutex_unlock(&pmuic->muic_mutex);
|
||||
|
||||
pr_info("%s val:%x buf%s\n", __func__, val, buf);
|
||||
|
||||
if (val < 0) {
|
||||
pr_err("%s: fail to read muic reg\n", __func__);
|
||||
return sprintf(buf, "UNKNOWN\n");
|
||||
}
|
||||
|
||||
return sprintf(buf, "%x\n", val);
|
||||
}
|
||||
|
||||
static ssize_t muic_set_otg_test(struct device *dev,
|
||||
struct device_attribute *pattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
muic_data_t *pmuic = dev_get_drvdata(dev);
|
||||
struct regmap_ops *pops = pmuic->regmapdesc->regmapops;
|
||||
struct reg_attr attr;
|
||||
int uattr;
|
||||
u8 val;
|
||||
|
||||
pr_info("%s buf:%s\n", __func__, buf);
|
||||
if (!strncmp(buf, "0", 1)) {
|
||||
val = 0;
|
||||
pmuic->is_otg_test = true;
|
||||
} else if (!strncmp(buf, "1", 1)) {
|
||||
val = 1;
|
||||
pmuic->is_otg_test = false;
|
||||
} else {
|
||||
pr_warn("%s:%s Wrong command\n", MUIC_DEV_NAME, __func__);
|
||||
return count;
|
||||
}
|
||||
|
||||
mutex_lock(&pmuic->muic_mutex);
|
||||
pops->ioctl(pmuic->regmapdesc, GET_OTG_STATUS, NULL, &uattr);
|
||||
_REG_ATTR(&attr, uattr);
|
||||
|
||||
val = muic_i2c_read_byte(pmuic->i2c, attr.addr);
|
||||
val |= attr.mask << attr.bitn;
|
||||
val |= _ATTR_OVERWRITE_M;
|
||||
val = regmap_write_value(pmuic->regmapdesc, uattr, val);
|
||||
mutex_unlock(&pmuic->muic_mutex);
|
||||
|
||||
if (val < 0) {
|
||||
pr_err("%s err writing %s reg(%d)\n", __func__,
|
||||
regmap_to_name(pmuic->regmapdesc, attr.addr), val);
|
||||
}
|
||||
|
||||
val = 0;
|
||||
val = muic_i2c_read_byte(pmuic->i2c, attr.addr);
|
||||
val &= (attr.mask << attr.bitn);
|
||||
pr_info("%s: %s(0x%02x)\n", __func__,
|
||||
regmap_to_name(pmuic->regmapdesc, attr.addr), val);
|
||||
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
static ssize_t muic_show_attached_dev(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
muic_data_t *pmuic = dev_get_drvdata(dev);
|
||||
|
||||
pr_info("%s:%s attached_dev:%d\n", MUIC_DEV_NAME, __func__,
|
||||
pmuic->attached_dev);
|
||||
|
||||
switch(pmuic->attached_dev) {
|
||||
case ATTACHED_DEV_NONE_MUIC:
|
||||
return sprintf(buf, "No VPS\n");
|
||||
case ATTACHED_DEV_USB_MUIC:
|
||||
return sprintf(buf, "USB\n");
|
||||
case ATTACHED_DEV_CDP_MUIC:
|
||||
return sprintf(buf, "CDP\n");
|
||||
case ATTACHED_DEV_OTG_MUIC:
|
||||
return sprintf(buf, "OTG\n");
|
||||
case ATTACHED_DEV_TA_MUIC:
|
||||
return sprintf(buf, "TA\n");
|
||||
case ATTACHED_DEV_JIG_UART_OFF_MUIC:
|
||||
return sprintf(buf, "JIG UART OFF\n");
|
||||
case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC:
|
||||
return sprintf(buf, "JIG UART OFF/VB\n");
|
||||
case ATTACHED_DEV_JIG_UART_ON_MUIC:
|
||||
return sprintf(buf, "JIG UART ON\n");
|
||||
case ATTACHED_DEV_JIG_USB_OFF_MUIC:
|
||||
return sprintf(buf, "JIG USB OFF\n");
|
||||
case ATTACHED_DEV_JIG_USB_ON_MUIC:
|
||||
return sprintf(buf, "JIG USB ON\n");
|
||||
case ATTACHED_DEV_DESKDOCK_MUIC:
|
||||
return sprintf(buf, "DESKDOCK\n");
|
||||
case ATTACHED_DEV_AUDIODOCK_MUIC:
|
||||
return sprintf(buf, "AUDIODOCK\n");
|
||||
case ATTACHED_DEV_CHARGING_CABLE_MUIC:
|
||||
return sprintf(buf, "PS CABLE\n");
|
||||
case ATTACHED_DEV_AFC_CHARGER_9V_MUIC:
|
||||
return sprintf(buf, "AFC charger\n");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return sprintf(buf, "UNKNOWN\n");
|
||||
}
|
||||
|
||||
static ssize_t muic_show_audio_path(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t muic_set_audio_path(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t muic_show_apo_factory(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
muic_data_t *pmuic = dev_get_drvdata(dev);
|
||||
const char *mode;
|
||||
|
||||
/* true: Factory mode, false: not Factory mode */
|
||||
if (pmuic->is_factory_start)
|
||||
mode = "FACTORY_MODE";
|
||||
else
|
||||
mode = "NOT_FACTORY_MODE";
|
||||
|
||||
pr_info("%s:%s apo factory=%s\n", MUIC_DEV_NAME, __func__, mode);
|
||||
|
||||
return sprintf(buf, "%s\n", mode);
|
||||
}
|
||||
|
||||
static ssize_t muic_set_apo_factory(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
muic_data_t *pmuic = dev_get_drvdata(dev);
|
||||
const char *mode;
|
||||
|
||||
pr_info("%s:%s buf:%s\n", MUIC_DEV_NAME, __func__, buf);
|
||||
|
||||
/* "FACTORY_START": factory mode */
|
||||
if (!strncmp(buf, "FACTORY_START", 13)) {
|
||||
pmuic->is_factory_start = true;
|
||||
mode = "FACTORY_MODE";
|
||||
} else {
|
||||
pr_warn("%s:%s Wrong command\n", MUIC_DEV_NAME, __func__);
|
||||
return count;
|
||||
}
|
||||
|
||||
pr_info("%s:%s apo factory=%s\n", MUIC_DEV_NAME, __func__, mode);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(uart_en, 0664, muic_show_uart_en, muic_set_uart_en);
|
||||
#if !defined(CONFIG_UART_SEL)
|
||||
static DEVICE_ATTR(uart_sel, 0664, muic_show_uart_sel,
|
||||
muic_set_uart_sel);
|
||||
static DEVICE_ATTR(usb_sel, 0664,
|
||||
muic_show_usb_sel, muic_set_usb_sel);
|
||||
#endif
|
||||
static DEVICE_ATTR(adc, 0664, muic_show_adc, NULL);
|
||||
#ifdef DEBUG_MUIC
|
||||
static DEVICE_ATTR(reg_dump, 0664, muic_show_registers, NULL);
|
||||
static DEVICE_ATTR(reg_sel, 0664, muic_show_reg_sel, muic_set_reg_sel);
|
||||
#endif
|
||||
static DEVICE_ATTR(usb_state, 0664, muic_show_usb_state, NULL);
|
||||
#if defined(CONFIG_USB_HOST_NOTIFY)
|
||||
static DEVICE_ATTR(otg_test, 0664,
|
||||
muic_show_otg_test, muic_set_otg_test);
|
||||
#endif
|
||||
static DEVICE_ATTR(attached_dev, 0664, muic_show_attached_dev, NULL);
|
||||
static DEVICE_ATTR(audio_path, 0664,
|
||||
muic_show_audio_path, muic_set_audio_path);
|
||||
static DEVICE_ATTR(apo_factory, 0664,
|
||||
muic_show_apo_factory,
|
||||
muic_set_apo_factory);
|
||||
|
||||
static struct attribute *muic_attributes[] = {
|
||||
&dev_attr_uart_en.attr,
|
||||
#if !defined(CONFIG_UART_SEL)
|
||||
&dev_attr_uart_sel.attr,
|
||||
&dev_attr_usb_sel.attr,
|
||||
#endif
|
||||
&dev_attr_adc.attr,
|
||||
#ifdef DEBUG_MUIC
|
||||
&dev_attr_reg_dump.attr,
|
||||
&dev_attr_reg_sel.attr,
|
||||
#endif
|
||||
&dev_attr_usb_state.attr,
|
||||
#if defined(CONFIG_USB_HOST_NOTIFY)
|
||||
&dev_attr_otg_test.attr,
|
||||
#endif
|
||||
&dev_attr_attached_dev.attr,
|
||||
&dev_attr_audio_path.attr,
|
||||
&dev_attr_apo_factory.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
const struct attribute_group muic_sysfs_group = {
|
||||
.attrs = muic_attributes,
|
||||
};
|
6
drivers/muic/universal/muic_sysfs.h
Normal file
6
drivers/muic/universal/muic_sysfs.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#ifndef _MUIC_SYSFS_
|
||||
#define _MUIC_SYSFS_
|
||||
|
||||
extern const struct attribute_group muic_sysfs_group;
|
||||
|
||||
#endif
|
664
drivers/muic/universal/muic_task.c
Normal file
664
drivers/muic/universal/muic_task.c
Normal file
|
@ -0,0 +1,664 @@
|
|||
/*
|
||||
* muic_task.c
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics
|
||||
* Thomas Ryu <smilesr.ryu@samsung.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/host_notify.h>
|
||||
|
||||
#include <linux/muic/muic.h>
|
||||
#include <linux/muic/muic_afc.h>
|
||||
|
||||
#if defined(CONFIG_MUIC_NOTIFIER)
|
||||
#include <linux/muic/muic_notifier.h>
|
||||
#endif /* CONFIG_MUIC_NOTIFIER */
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
#include "muic-internal.h"
|
||||
#include "muic_apis.h"
|
||||
#include "muic_state.h"
|
||||
#include "muic_vps.h"
|
||||
#include "muic_i2c.h"
|
||||
#include "muic_sysfs.h"
|
||||
#include "muic_debug.h"
|
||||
#include "muic_dt.h"
|
||||
#include "muic_regmap.h"
|
||||
|
||||
#define MUIC_INT_DETACH_MASK (0x1 << 1)
|
||||
#define MUIC_INT_ATTACH_MASK (0x1 << 0)
|
||||
#if !defined(CONFIG_MUIC_UNIVERSAL_SM5504)
|
||||
#define MUIC_INT_OVP_EN_MASK (0x1 << 5)
|
||||
#define MUIC_INT_VBUS_OFF_MASK (0x1 << 0)
|
||||
#else
|
||||
#define MUIC_INT_OVP_EN_MASK (0x1 << 7)
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_SM5504)
|
||||
#define MUIC_REG_CTRL_RESET_VALUE (0xE5)
|
||||
#else
|
||||
#define MUIC_REG_CTRL_RESET_VALUE (0x1F)
|
||||
#endif
|
||||
|
||||
#define MUIC_REG_CTRL 0x02
|
||||
#define MUIC_REG_INT1 0x03
|
||||
#define MUIC_REG_INT2 0x04
|
||||
#define MUIC_REG_INT3 0x05
|
||||
|
||||
extern struct muic_platform_data muic_pdata;
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_SM5705_AFC)
|
||||
/* SM5705 Interrupt 3 AFC register */
|
||||
#define INT3_AFC_ERROR_SHIFT 5
|
||||
#define INT3_AFC_STA_CHG_SHIFT 4
|
||||
#define INT3_AFC_MULTI_BYTE_SHIFT 3
|
||||
#define INT3_AFC_VBUS_UPDATE_SHIFT 2
|
||||
#define INT3_AFC_ACCEPTED_SHIFT 1
|
||||
#define INT3_AFC_TA_ATTACHED_SHIFT 0
|
||||
|
||||
#define INT3_AFC_ERROR_MASK (1 << INT3_AFC_ERROR_SHIFT)
|
||||
#define INT3_AFC_STA_CHG_MASK (1 << INT3_AFC_STA_CHG_SHIFT)
|
||||
#define INT3_AFC_MULTI_BYTE_MASK (1 << INT3_AFC_MULTI_BYTE_SHIFT)
|
||||
#define INT3_AFC_VBUS_UPDATE_MASK (1 << INT3_AFC_VBUS_UPDATE_SHIFT)
|
||||
#define INT3_AFC_ACCEPTED_MASK (1 << INT3_AFC_ACCEPTED_SHIFT)
|
||||
#define INT3_AFC_TA_ATTACHED_MASK (1 << INT3_AFC_TA_ATTACHED_SHIFT)
|
||||
|
||||
static int muic_irq_handler_afc(muic_data_t *pmuic, int irq)
|
||||
{
|
||||
struct i2c_client *i2c = pmuic->i2c;
|
||||
struct afc_ops *afcops = pmuic->regmapdesc->afcops;
|
||||
int intr1, intr2, intr3, ret;
|
||||
|
||||
pr_info("%s:%s irq(%d)\n", pmuic->chip_name, __func__, irq);
|
||||
|
||||
/* read and clear interrupt status bits */
|
||||
intr1 = muic_i2c_read_byte(i2c, MUIC_REG_INT1);
|
||||
intr2 = muic_i2c_read_byte(i2c, MUIC_REG_INT2);
|
||||
intr3 = muic_i2c_read_byte(i2c, MUIC_REG_INT3);
|
||||
|
||||
if ((intr1 < 0) || (intr2 < 0)) {
|
||||
pr_err("%s: err read interrupt status [1:0x%x, 2:0x%x]\n",
|
||||
__func__, intr1, intr2);
|
||||
return INT_REQ_DISCARD;
|
||||
}
|
||||
|
||||
if (intr1 & MUIC_INT_ATTACH_MASK)
|
||||
{
|
||||
int intr_tmp;
|
||||
intr_tmp = muic_i2c_read_byte(i2c, MUIC_REG_INT1);
|
||||
if (intr_tmp & 0x2)
|
||||
{
|
||||
pr_info("%s:%s attach/detach interrupt occurred\n",
|
||||
pmuic->chip_name, __func__);
|
||||
intr1 &= 0xFE;
|
||||
}
|
||||
intr1 |= intr_tmp;
|
||||
}
|
||||
if (intr1 & MUIC_INT_DETACH_MASK) {
|
||||
cancel_delayed_work(&pmuic->afc_retry_work);
|
||||
cancel_delayed_work(&pmuic->afc_restart_work);
|
||||
}
|
||||
|
||||
pr_info("%s:%s intr[1:0x%x, 2:0x%x, 3:0x%x]\n", pmuic->chip_name, __func__,
|
||||
intr1, intr2, intr3);
|
||||
|
||||
/* check for muic reset and recover for every interrupt occurred */
|
||||
if ((intr1 & MUIC_INT_OVP_EN_MASK) ||
|
||||
((intr1 == 0) && (intr2 == 0) && (intr3 == 0) && (irq != -1)))
|
||||
{
|
||||
int ctrl;
|
||||
ctrl = muic_i2c_read_byte(i2c, MUIC_REG_CTRL);
|
||||
if (ctrl == 0x1F)
|
||||
{
|
||||
/* CONTROL register is reset to 1F */
|
||||
muic_print_reg_log();
|
||||
muic_print_reg_dump(pmuic);
|
||||
pr_err("%s: err muic could have been reseted. Initilize!!\n",
|
||||
__func__);
|
||||
muic_reg_init(pmuic);
|
||||
muic_print_reg_dump(pmuic);
|
||||
|
||||
/* MUIC Interrupt On */
|
||||
set_int_mask(pmuic, false);
|
||||
}
|
||||
|
||||
if ((intr1 & MUIC_INT_ATTACH_MASK) == 0)
|
||||
return INT_REQ_DISCARD;
|
||||
}
|
||||
|
||||
pmuic->intr.intr1 = intr1;
|
||||
pmuic->intr.intr2 = intr2;
|
||||
pmuic->intr.intr3 = intr3;
|
||||
|
||||
if ((irq == -1) && (intr3 & INT3_AFC_TA_ATTACHED_MASK))
|
||||
{
|
||||
ret = muic_i2c_write_byte(i2c, 0x0F, 0x01);
|
||||
if(ret < 0){
|
||||
pr_err("%s: err write register value \n", __func__);
|
||||
return INT_REQ_DISCARD;
|
||||
}
|
||||
return INT_REQ_DONE;
|
||||
}
|
||||
|
||||
if ((intr1 & MUIC_INT_DETACH_MASK) && (intr2 & MUIC_INT_VBUS_OFF_MASK))
|
||||
return INT_REQ_DONE;
|
||||
|
||||
if (intr3 & INT3_AFC_TA_ATTACHED_MASK) { /*AFC_TA_ATTACHED*/
|
||||
return afcops->afc_ta_attach(pmuic->regmapdesc);
|
||||
} else if (intr3 & INT3_AFC_ACCEPTED_MASK) { /*AFC_ACCEPTED*/
|
||||
return afcops->afc_ta_accept(pmuic->regmapdesc);
|
||||
} else if (intr3 & INT3_AFC_VBUS_UPDATE_MASK) { /*AFC_VBUS_UPDATE*/
|
||||
return afcops->afc_vbus_update(pmuic->regmapdesc);
|
||||
} else if (intr3 & INT3_AFC_MULTI_BYTE_MASK) { /*AFC_MULTI_BYTE*/
|
||||
return afcops->afc_multi_byte(pmuic->regmapdesc);
|
||||
} else if (intr3 & INT3_AFC_ERROR_MASK) { /*AFC_ERROR*/
|
||||
cancel_delayed_work(&pmuic->afc_retry_work);
|
||||
return afcops->afc_error(pmuic->regmapdesc);
|
||||
} else if (intr3 & INT3_AFC_STA_CHG_MASK) { /*AFC_STA_CHG*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
return INT_REQ_DONE;
|
||||
}
|
||||
#else
|
||||
static int muic_irq_handler(muic_data_t *pmuic, int irq)
|
||||
{
|
||||
struct i2c_client *i2c = pmuic->i2c;
|
||||
int intr1, intr2, ctrl = 0;
|
||||
|
||||
pr_info("%s:%s irq(%d)\n", pmuic->chip_name, __func__, irq);
|
||||
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_SM5504)
|
||||
/* SM5504 needs 100ms delay */
|
||||
msleep(100);
|
||||
#endif
|
||||
|
||||
/* read and clear interrupt status bits */
|
||||
intr1 = muic_i2c_read_byte(i2c, MUIC_REG_INT1);
|
||||
intr2 = muic_i2c_read_byte(i2c, MUIC_REG_INT2);
|
||||
|
||||
if ((intr1 < 0) || (intr2 < 0)) {
|
||||
pr_err("%s: err read interrupt status [1:0x%x, 2:0x%x]\n",
|
||||
__func__, intr1, intr2);
|
||||
return INT_REQ_DISCARD;
|
||||
}
|
||||
|
||||
if (intr1 & MUIC_INT_ATTACH_MASK) {
|
||||
int intr_tmp;
|
||||
intr_tmp = muic_i2c_read_byte(i2c, MUIC_REG_INT1);
|
||||
if (intr_tmp & 0x2) {
|
||||
pr_info("%s:%s attach/detach interrupt occurred\n",
|
||||
pmuic->chip_name, __func__);
|
||||
intr1 &= 0xFE;
|
||||
}
|
||||
intr1 |= intr_tmp;
|
||||
}
|
||||
|
||||
pr_info("%s:%s intr[1:0x%x, 2:0x%x]\n", pmuic->chip_name, __func__,
|
||||
intr1, intr2);
|
||||
|
||||
ctrl = muic_i2c_read_byte(i2c, MUIC_REG_CTRL);
|
||||
|
||||
/* check for muic reset and recover for every interrupt occurred */
|
||||
if ((intr1 & MUIC_INT_OVP_EN_MASK) ||
|
||||
((ctrl == MUIC_REG_CTRL_RESET_VALUE) && (irq != -1))) {
|
||||
if (ctrl == MUIC_REG_CTRL_RESET_VALUE) {
|
||||
/* CONTROL register is reset to 1F */
|
||||
muic_print_reg_log();
|
||||
muic_print_reg_dump(pmuic);
|
||||
pr_err("%s: err muic could have been reseted. Initilize!!\n",
|
||||
__func__);
|
||||
muic_reg_init(pmuic);
|
||||
muic_print_reg_dump(pmuic);
|
||||
|
||||
/* MUIC Interrupt On */
|
||||
set_int_mask(pmuic, false);
|
||||
}
|
||||
|
||||
if ((intr1 & MUIC_INT_ATTACH_MASK) == 0)
|
||||
return INT_REQ_DISCARD;
|
||||
}
|
||||
|
||||
pmuic->intr.intr1 = intr1;
|
||||
pmuic->intr.intr2 = intr2;
|
||||
|
||||
return INT_REQ_DONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
enum max_intr_bits {
|
||||
INTR1_ADC1K_MASK = (1<<3),
|
||||
INTR1_ADCERR_MASK = (1<<2),
|
||||
INTR1_ADC_MASK = (1<<0),
|
||||
|
||||
INTR2_VBVOLT_MASK = (1<<4),
|
||||
INTR2_OVP_MASK = (1<<3),
|
||||
INTR2_DCTTMR_MASK = (1<<2),
|
||||
INTR2_CHGDETRUN_MASK = (1<<1),
|
||||
INTR2_CHGTYP_MASK = (1<<0),
|
||||
};
|
||||
|
||||
#define MAX77849_REG_INT1 (0x01)
|
||||
#define MAX77849_REG_INT2 (0x02)
|
||||
|
||||
static irqreturn_t max77849_muic_irq_handler(muic_data_t *pmuic, int irq)
|
||||
{
|
||||
u8 intr1, intr2;
|
||||
|
||||
pr_info("%s:%s irq:%d\n", MUIC_DEV_NAME, __func__, irq);
|
||||
|
||||
intr1 = muic_i2c_read_byte(pmuic->i2c, MAX77849_REG_INT1);
|
||||
intr2 = muic_i2c_read_byte(pmuic->i2c, MAX77849_REG_INT2);
|
||||
|
||||
if (intr1 & INTR1_ADC1K_MASK)
|
||||
pr_info("%s ADC1K interrupt occured\n", __func__);
|
||||
|
||||
if (intr1 & INTR1_ADCERR_MASK)
|
||||
pr_info("%s ADC1K interrupt occured\n", __func__);
|
||||
|
||||
if (intr1 & INTR1_ADCERR_MASK)
|
||||
pr_info("%s ADCERR interrupt occured\n", __func__);
|
||||
|
||||
if (intr2 & INTR2_VBVOLT_MASK)
|
||||
pr_info("%s VBVOLT interrupt occured\n", __func__);
|
||||
|
||||
if (intr2 & INTR2_OVP_MASK)
|
||||
pr_info("%s OVP interrupt occured\n", __func__);
|
||||
|
||||
if (intr2 & INTR2_DCTTMR_MASK)
|
||||
pr_info("%s DCTTMR interrupt occured\n", __func__);
|
||||
|
||||
if (intr2 & INTR2_CHGTYP_MASK)
|
||||
pr_info("%s DCTTMR interrupt occured\n", __func__);
|
||||
|
||||
return INT_REQ_DONE;
|
||||
}
|
||||
|
||||
static irqreturn_t muic_irq_thread(int irq, void *data)
|
||||
{
|
||||
muic_data_t *pmuic = data;
|
||||
|
||||
mutex_lock(&pmuic->muic_mutex);
|
||||
pmuic->irq_n = irq;
|
||||
if (pmuic->vps_table == VPS_TYPE_TABLE) {
|
||||
if (max77849_muic_irq_handler(pmuic, irq) & INT_REQ_DONE)
|
||||
muic_detect_dev(pmuic);
|
||||
} else{
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_SM5705_AFC)
|
||||
if (muic_irq_handler_afc(pmuic, irq) & INT_REQ_DONE)
|
||||
muic_detect_dev(pmuic);
|
||||
#else
|
||||
if (muic_irq_handler(pmuic, irq) & INT_REQ_DONE)
|
||||
muic_detect_dev(pmuic);
|
||||
#endif
|
||||
}
|
||||
|
||||
mutex_unlock(&pmuic->muic_mutex);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void muic_init_detect(struct work_struct *work)
|
||||
{
|
||||
muic_data_t *pmuic =
|
||||
container_of(work, muic_data_t, init_work.work);
|
||||
|
||||
pr_info("%s:%s\n", pmuic->chip_name, __func__);
|
||||
|
||||
/* MUIC Interrupt On */
|
||||
set_int_mask(pmuic, false);
|
||||
|
||||
muic_irq_thread(-1, pmuic);
|
||||
}
|
||||
|
||||
static int muic_irq_init(muic_data_t *pmuic)
|
||||
{
|
||||
struct i2c_client *i2c = pmuic->i2c;
|
||||
struct muic_platform_data *pdata = pmuic->pdata;
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s\n", pmuic->chip_name, __func__);
|
||||
|
||||
if (!pdata->irq_gpio) {
|
||||
pr_warn("%s:%s No interrupt specified\n", pmuic->chip_name,
|
||||
__func__);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
i2c->irq = gpio_to_irq(pdata->irq_gpio);
|
||||
|
||||
if (i2c->irq) {
|
||||
ret = request_threaded_irq(i2c->irq, NULL,
|
||||
muic_irq_thread,
|
||||
(IRQF_TRIGGER_FALLING | IRQF_ONESHOT),
|
||||
"muic-irq", pmuic);
|
||||
if (ret < 0) {
|
||||
pr_err("%s:%s failed to reqeust IRQ(%d)\n",
|
||||
pmuic->chip_name, __func__, i2c->irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = enable_irq_wake(i2c->irq);
|
||||
if (ret < 0)
|
||||
pr_err("%s:%s failed to enable wakeup src\n",
|
||||
pmuic->chip_name, __func__);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int muic_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(i2c->dev.parent);
|
||||
struct muic_platform_data *pdata = &muic_pdata;
|
||||
muic_data_t *pmuic;
|
||||
struct regmap_desc *pdesc = NULL;
|
||||
struct regmap_ops *pops = NULL;
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
pr_err("%s: i2c functionality check error\n", __func__);
|
||||
ret = -EIO;
|
||||
goto err_return;
|
||||
}
|
||||
|
||||
pmuic = kzalloc(sizeof(muic_data_t), GFP_KERNEL);
|
||||
if (!pmuic) {
|
||||
pr_err("%s: failed to allocate driver data\n", __func__);
|
||||
ret = -ENOMEM;
|
||||
goto err_return;
|
||||
}
|
||||
|
||||
i2c->dev.platform_data = pdata;
|
||||
|
||||
i2c_set_clientdata(i2c, pmuic);
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
ret = of_muic_dt(i2c, pdata);
|
||||
if (ret < 0) {
|
||||
pr_err("%s:%s Failed to get device of_node \n",
|
||||
MUIC_DEV_NAME, __func__);
|
||||
goto err_io;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MUIC_PINCTRL)
|
||||
ret = of_muic_pinctrl(i2c);
|
||||
if (ret < 0) {
|
||||
pr_err("%s:%s Failed to set pinctrl\n",
|
||||
MUIC_DEV_NAME, __func__);
|
||||
goto err_io;
|
||||
}
|
||||
#endif
|
||||
|
||||
of_update_supported_list(i2c, pdata);
|
||||
vps_show_table();
|
||||
#endif /* CONFIG_OF */
|
||||
if (!pdata) {
|
||||
pr_err("%s: failed to get i2c platform data\n", __func__);
|
||||
ret = -ENODEV;
|
||||
goto err_io;
|
||||
}
|
||||
|
||||
mutex_init(&pmuic->muic_mutex);
|
||||
|
||||
pmuic->pdata = pdata;
|
||||
pmuic->i2c = i2c;
|
||||
pmuic->is_factory_start = false;
|
||||
pmuic->is_otg_test = false;
|
||||
pmuic->attached_dev = ATTACHED_DEV_UNKNOWN_MUIC;
|
||||
pmuic->is_usb_ready = false;
|
||||
#ifdef CONFIG_UART_SEL
|
||||
pmuic->pdata->muic_set_path = java_muic_set_path;
|
||||
#endif
|
||||
|
||||
if (!strcmp(pmuic->chip_name, "max,max77849"))
|
||||
pmuic->vps_table = VPS_TYPE_TABLE;
|
||||
else
|
||||
pmuic->vps_table = VPS_TYPE_SCATTERED;
|
||||
|
||||
pr_info("%s: VPS_TYPE=%d\n", __func__, pmuic->vps_table);
|
||||
|
||||
if (!pdata->set_gpio_uart_sel) {
|
||||
if (pmuic->gpio_uart_sel) {
|
||||
pr_info("%s: gpio_uart_sel registered.\n", __func__);
|
||||
pdata->set_gpio_uart_sel = muic_set_gpio_uart_sel;
|
||||
} else
|
||||
pr_info("%s: gpio_uart_sel is not supported.\n", __func__);
|
||||
}
|
||||
|
||||
if (pmuic->pdata->init_gpio_cb) {
|
||||
ret = pmuic->pdata->init_gpio_cb();
|
||||
if (ret) {
|
||||
pr_err("%s: failed to init gpio(%d)\n", __func__, ret);
|
||||
goto fail_init_gpio;
|
||||
}
|
||||
}
|
||||
|
||||
if (pmuic->pdata->init_switch_dev_cb)
|
||||
pmuic->pdata->init_switch_dev_cb();
|
||||
|
||||
if (!(get_switch_sel() & SWITCH_SEL_RUSTPROOF_MASK)) {
|
||||
pr_info(" Enable rustproof mode\n");
|
||||
pmuic->is_rustproof = true;
|
||||
} else {
|
||||
pr_info(" Disable rustproof mode\n");
|
||||
pmuic->is_rustproof = false;
|
||||
}
|
||||
|
||||
/* Register chipset register map. */
|
||||
muic_register_regmap(&pdesc, pmuic);
|
||||
pdesc->muic = pmuic;
|
||||
pops = pdesc->regmapops;
|
||||
pmuic->regmapdesc = pdesc;
|
||||
|
||||
/* set switch device's driver_data */
|
||||
dev_set_drvdata(switch_device, pmuic);
|
||||
|
||||
/* create sysfs group */
|
||||
ret = sysfs_create_group(&switch_device->kobj, &muic_sysfs_group);
|
||||
if (ret) {
|
||||
pr_err("%s: failed to create sm5703 muic attribute group\n",
|
||||
__func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = pops->revision(pdesc);
|
||||
if (ret) {
|
||||
pr_err("%s: failed to init muic rev register(%d)\n", __func__,
|
||||
ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = muic_reg_init(pmuic);
|
||||
if (ret) {
|
||||
pr_err("%s: failed to init muic register(%d)\n", __func__, ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pops->update(pdesc);
|
||||
pops->show(pdesc);
|
||||
|
||||
ret = muic_irq_init(pmuic);
|
||||
if (ret) {
|
||||
pr_err("%s: failed to init muic irq(%d)\n", __func__, ret);
|
||||
goto fail_init_irq;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_SM5705_AFC)
|
||||
muic_init_afc_state(pmuic);
|
||||
#endif
|
||||
/* initial cable detection */
|
||||
INIT_DELAYED_WORK(&pmuic->init_work, muic_init_detect);
|
||||
schedule_delayed_work(&pmuic->init_work, msecs_to_jiffies(300));
|
||||
#ifdef DEBUG_MUIC
|
||||
INIT_DELAYED_WORK(&pmuic->usb_work, muic_show_debug_info);
|
||||
schedule_delayed_work(&pmuic->usb_work, msecs_to_jiffies(10000));
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
fail_init_irq:
|
||||
if (i2c->irq)
|
||||
free_irq(i2c->irq, pmuic);
|
||||
fail:
|
||||
if (pmuic->pdata->cleanup_switch_dev_cb)
|
||||
pmuic->pdata->cleanup_switch_dev_cb();
|
||||
sysfs_remove_group(&switch_device->kobj, &muic_sysfs_group);
|
||||
mutex_unlock(&pmuic->muic_mutex);
|
||||
mutex_destroy(&pmuic->muic_mutex);
|
||||
fail_init_gpio:
|
||||
i2c_set_clientdata(i2c, NULL);
|
||||
err_io:
|
||||
kfree(pmuic);
|
||||
err_return:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int muic_remove(struct i2c_client *i2c)
|
||||
{
|
||||
muic_data_t *pmuic = i2c_get_clientdata(i2c);
|
||||
|
||||
sysfs_remove_group(&switch_device->kobj, &muic_sysfs_group);
|
||||
|
||||
if (pmuic) {
|
||||
pr_info("%s:%s\n", pmuic->chip_name, __func__);
|
||||
cancel_delayed_work(&pmuic->init_work);
|
||||
cancel_delayed_work(&pmuic->usb_work);
|
||||
disable_irq_wake(pmuic->i2c->irq);
|
||||
free_irq(pmuic->i2c->irq, pmuic);
|
||||
|
||||
if (pmuic->pdata->cleanup_switch_dev_cb)
|
||||
pmuic->pdata->cleanup_switch_dev_cb();
|
||||
|
||||
mutex_destroy(&pmuic->muic_mutex);
|
||||
i2c_set_clientdata(pmuic->i2c, NULL);
|
||||
kfree(pmuic);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id muic_i2c_id[] = {
|
||||
{ MUIC_DEV_NAME, 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, muic_i2c_id);
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
MODULE_DEVICE_TABLE(of, muic_i2c_dt_ids);
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static void muic_shutdown(struct i2c_client *i2c)
|
||||
{
|
||||
muic_data_t *pmuic = i2c_get_clientdata(i2c);
|
||||
int ret;
|
||||
|
||||
pr_info("%s:%s\n", pmuic->chip_name, __func__);
|
||||
if (!pmuic->i2c) {
|
||||
pr_err("%s:%s no muic i2c client\n", pmuic->chip_name, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
pr_info("%s:%s open D+,D-\n", pmuic->chip_name, __func__);
|
||||
ret = com_to_open_with_vbus(pmuic);
|
||||
if (ret < 0)
|
||||
pr_err("%s:%s fail to open mansw1 reg\n", pmuic->chip_name,
|
||||
__func__);
|
||||
|
||||
/* set auto sw mode before shutdown to make sure device goes into */
|
||||
/* LPM charging when TA or USB is connected during off state */
|
||||
pr_info("%s:%s muic auto detection enable\n", pmuic->chip_name, __func__);
|
||||
set_switch_mode(pmuic, SWMODE_AUTO);
|
||||
|
||||
if (pmuic->pdata && pmuic->pdata->cleanup_switch_dev_cb)
|
||||
pmuic->pdata->cleanup_switch_dev_cb();
|
||||
|
||||
pr_info("%s:%s -\n", MUIC_DEV_NAME, __func__);
|
||||
}
|
||||
#if defined(CONFIG_PM)
|
||||
|
||||
static int muic_suspend(struct device *dev)
|
||||
{
|
||||
muic_data_t *pmuic = dev_get_drvdata(dev);
|
||||
struct i2c_client *i2c = pmuic->i2c;
|
||||
|
||||
disable_irq_nosync(i2c->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int muic_resume(struct device *dev)
|
||||
{
|
||||
muic_data_t *pmuic = dev_get_drvdata(dev);
|
||||
struct i2c_client *i2c = pmuic->i2c;
|
||||
|
||||
enable_irq(i2c->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct dev_pm_ops muic_pm = {
|
||||
.suspend = muic_suspend,
|
||||
.resume = muic_resume,
|
||||
};
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static struct i2c_driver muic_driver = {
|
||||
.driver = {
|
||||
.name = MUIC_DEV_NAME,
|
||||
#if defined(CONFIG_OF)
|
||||
.of_match_table = muic_i2c_dt_ids,
|
||||
#endif /* CONFIG_OF */
|
||||
#if defined(CONFIG_PM)
|
||||
.pm = &muic_pm,
|
||||
#endif /* CONFIG_PM */
|
||||
},
|
||||
.probe = muic_probe,
|
||||
.remove = muic_remove,
|
||||
.shutdown = muic_shutdown,
|
||||
.id_table = muic_i2c_id,
|
||||
};
|
||||
|
||||
static int __init muic_init(void)
|
||||
{
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
return i2c_add_driver(&muic_driver);
|
||||
}
|
||||
module_init(muic_init);
|
||||
|
||||
static void muic_exit(void)
|
||||
{
|
||||
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
|
||||
i2c_del_driver(&muic_driver);
|
||||
}
|
||||
module_exit(muic_exit);
|
||||
|
||||
MODULE_DESCRIPTION("MUIC driver");
|
||||
MODULE_AUTHOR("<smilesr.ryu@samsung.com>");
|
||||
MODULE_LICENSE("GPL");
|
807
drivers/muic/universal/muic_vps.c
Normal file
807
drivers/muic/universal/muic_vps.c
Normal file
|
@ -0,0 +1,807 @@
|
|||
/*
|
||||
* muic_vps.c
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics
|
||||
* Thomas Ryu <smilesr.ryu@samsung.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/host_notify.h>
|
||||
|
||||
#include <linux/muic/muic.h>
|
||||
|
||||
#if defined(CONFIG_MUIC_NOTIFIER)
|
||||
#include <linux/muic/muic_notifier.h>
|
||||
#endif /* CONFIG_MUIC_NOTIFIER */
|
||||
|
||||
#if defined (CONFIG_OF)
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
#include "muic-internal.h"
|
||||
#include "muic_apis.h"
|
||||
#include "muic_i2c.h"
|
||||
#include "muic_vps.h"
|
||||
|
||||
/* Device Type 1 register */
|
||||
#define DEV_TYPE1_USB_OTG (0x1 << 7)
|
||||
#define DEV_TYPE1_DEDICATED_CHG (0x1 << 6)
|
||||
#define DEV_TYPE1_CDP (0x1 << 5)
|
||||
#define DEV_TYPE1_T1_T2_CHG (0x1 << 4)
|
||||
#define DEV_TYPE1_UART (0x1 << 3)
|
||||
#define DEV_TYPE1_USB (0x1 << 2)
|
||||
#define DEV_TYPE1_AUDIO_2 (0x1 << 1)
|
||||
#define DEV_TYPE1_AUDIO_1 (0x1 << 0)
|
||||
#define DEV_TYPE1_USB_TYPES (DEV_TYPE1_USB_OTG | DEV_TYPE1_CDP | \
|
||||
DEV_TYPE1_USB)
|
||||
#define DEV_TYPE1_CHG_TYPES (DEV_TYPE1_DEDICATED_CHG | DEV_TYPE1_CDP)
|
||||
|
||||
/* Device Type 2 register */
|
||||
#define DEV_TYPE2_AV (0x1 << 6)
|
||||
#define DEV_TYPE2_TTY (0x1 << 5)
|
||||
#define DEV_TYPE2_PPD (0x1 << 4)
|
||||
#define DEV_TYPE2_JIG_UART_OFF (0x1 << 3)
|
||||
#define DEV_TYPE2_JIG_UART_ON (0x1 << 2)
|
||||
#define DEV_TYPE2_JIG_USB_OFF (0x1 << 1)
|
||||
#define DEV_TYPE2_JIG_USB_ON (0x1 << 0)
|
||||
#define DEV_TYPE2_JIG_USB_TYPES (DEV_TYPE2_JIG_USB_OFF | \
|
||||
DEV_TYPE2_JIG_USB_ON)
|
||||
#define DEV_TYPE2_JIG_UART_TYPES (DEV_TYPE2_JIG_UART_OFF)
|
||||
#define DEV_TYPE2_JIG_TYPES (DEV_TYPE2_JIG_UART_TYPES | \
|
||||
DEV_TYPE2_JIG_USB_TYPES)
|
||||
|
||||
/* Device Type 3 register */
|
||||
#define DEV_TYPE3_U200_CHG (0x1 << 6)
|
||||
#define DEV_TYPE3_AV_WITH_VBUS (0x1 << 4)
|
||||
#define DEV_TYPE3_NO_STD_CHG (0x1 << 2)
|
||||
#define DEV_TYPE3_MHL (0x1 << 0)
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_SM5705)
|
||||
#define DEV_TYPE3_LO_TA (0x1 << 5)
|
||||
#define DEV_TYPE3_CHG_TYPE (DEV_TYPE3_U200_CHG | DEV_TYPE3_NO_STD_CHG | \
|
||||
DEV_TYPE3_LO_TA)
|
||||
#else
|
||||
#define DEV_TYPE3_CHG_TYPE (DEV_TYPE3_U200_CHG | DEV_TYPE3_NO_STD_CHG)
|
||||
#endif
|
||||
|
||||
static struct vps_cfg cfg_MHL = {
|
||||
.name = "MHL",
|
||||
.attr = MATTR(VCOM_OPEN, VB_ANY)
|
||||
};
|
||||
static struct vps_cfg cfg_OTG = {
|
||||
.name = "OTG",
|
||||
.attr = MATTR(VCOM_OPEN, VB_CHK),
|
||||
};
|
||||
static struct vps_cfg cfg_VZW_ACC = {
|
||||
.name = "VZW Accessory",
|
||||
.attr = MATTR(VCOM_OPEN, VB_ANY),
|
||||
};
|
||||
static struct vps_cfg cfg_VZW_INCOMPATIBLE = {
|
||||
.name = "VZW Incompatible",
|
||||
.attr = MATTR(VCOM_OPEN, VB_ANY),
|
||||
};
|
||||
static struct vps_cfg cfg_SMARTDOCK = {
|
||||
.name = "Smartdock",
|
||||
.attr = MATTR(VCOM_OPEN, VB_CHK),
|
||||
};
|
||||
static struct vps_cfg cfg_HMT = {
|
||||
.name = "HMT",
|
||||
.attr = MATTR(VCOM_USB, VB_ANY),
|
||||
};
|
||||
static struct vps_cfg cfg_AUDIODOCK = {
|
||||
.name = "Audiodock",
|
||||
.attr = MATTR(VCOM_USB, VB_HIGH),
|
||||
};
|
||||
static struct vps_cfg cfg_USB_LANHUB = {
|
||||
.name = "USB LANHUB",
|
||||
.attr = MATTR(VCOM_OPEN, VB_ANY),
|
||||
};
|
||||
static struct vps_cfg cfg_CHARGING_CABLE = {
|
||||
.name = "Charging Cable",
|
||||
.attr = MATTR(VCOM_USB, VB_ANY),
|
||||
};
|
||||
static struct vps_cfg cfg_UNIVERSAL_MMDOCK = {
|
||||
.name = "Universal Multimedia dock",
|
||||
.attr = MATTR(VCOM_USB, VB_HIGH),
|
||||
};
|
||||
static struct vps_cfg cfg_JIG_USB_OFF = {
|
||||
.name = "Jig USB Off",
|
||||
.attr = MATTR(VCOM_USB, VB_HIGH) | MATTR_FACT_SUPP,
|
||||
};
|
||||
static struct vps_cfg cfg_JIG_USB_ON = {
|
||||
.name = "Jig USB On",
|
||||
.attr = MATTR(VCOM_USB, VB_HIGH) | MATTR_FACT_SUPP,
|
||||
};
|
||||
static struct vps_cfg cfg_DESKDOCK = {
|
||||
.name = "Deskdock",
|
||||
.attr = MATTR(VCOM_OPEN, VB_ANY),
|
||||
};
|
||||
static struct vps_cfg cfg_TYPE2_CHG = {
|
||||
.name = "TYPE2 Charger",
|
||||
.attr = MATTR(VCOM_OPEN, VB_ANY),
|
||||
};
|
||||
static struct vps_cfg cfg_JIG_UART_OFF = {
|
||||
.name = "Jig UART Off",
|
||||
.attr = MATTR(VCOM_UART, VB_ANY) | MATTR_FACT_SUPP,
|
||||
};
|
||||
//CONFIG_SEC_FACTORY
|
||||
static struct vps_cfg cfg_JIG_UART_ON = {
|
||||
.name = "Jig UART On",
|
||||
.attr = MATTR(VCOM_UART, VB_ANY) | MATTR_FACT_SUPP,
|
||||
};
|
||||
static struct vps_cfg cfg_TA = {
|
||||
.name = "TA",
|
||||
.attr = MATTR(VCOM_OPEN, VB_HIGH) | MATTR_CDET_SUPP,
|
||||
};
|
||||
static struct vps_cfg cfg_USB = {
|
||||
.name = "USB",
|
||||
.attr = MATTR(VCOM_OPEN, VB_HIGH) | MATTR_CDET_SUPP,
|
||||
};
|
||||
static struct vps_cfg cfg_CDP = {
|
||||
.name = "CDP",
|
||||
.attr = MATTR(VCOM_OPEN, VB_HIGH) | MATTR_CDET_SUPP,
|
||||
};
|
||||
static struct vps_cfg cfg_UNDEFINED_CHARGING = {
|
||||
.name = "Undefined Charging",
|
||||
.attr = MATTR(VCOM_OPEN, VB_HIGH) | MATTR_SUPP,
|
||||
};
|
||||
|
||||
static struct vps_tbl_data vps_table[] = {
|
||||
[MDEV(OTG)] = {0x00, "GND", &cfg_OTG,},
|
||||
[MDEV(MHL)] = {0xfe, "1K", &cfg_MHL,},
|
||||
/* 0x01 ~ 0x0D : Remote Sx Button */
|
||||
[MDEV(VZW_ACC)] = {0x0e, "28.7K", &cfg_VZW_ACC,},
|
||||
[MDEV(VZW_INCOMPATIBLE)] = {0x0f, "34K", &cfg_VZW_INCOMPATIBLE,},
|
||||
[MDEV(SMARTDOCK)] = {0x10, "40.2K", &cfg_SMARTDOCK,},
|
||||
[MDEV(HMT)] = {0x11, "49.9K", &cfg_HMT,},
|
||||
[MDEV(AUDIODOCK)] = {0x12, "64.9K", &cfg_AUDIODOCK,},
|
||||
[MDEV(USB_LANHUB)] = {0x13, "80.07K", &cfg_USB_LANHUB,},
|
||||
[MDEV(CHARGING_CABLE)] = {0x14, "102K", &cfg_CHARGING_CABLE,},
|
||||
[MDEV(UNIVERSAL_MMDOCK)] = {0x15, "121K", &cfg_UNIVERSAL_MMDOCK,},
|
||||
/* 0x16: UART Cable */
|
||||
/* 0x17: CEA-936A Type 1 Charger */
|
||||
[MDEV(JIG_USB_OFF)] = {0x18, "255K", &cfg_JIG_USB_OFF,},
|
||||
[MDEV(JIG_USB_ON)] = {0x19, "301K", &cfg_JIG_USB_ON,},
|
||||
[MDEV(DESKDOCK)] = {0x1a, "365K", &cfg_DESKDOCK,},
|
||||
[MDEV(TYPE2_CHG)] = {0x1b, "442K", &cfg_TYPE2_CHG,},
|
||||
[MDEV(JIG_UART_OFF)] = {0x1c, "523K", &cfg_JIG_UART_OFF,},
|
||||
[MDEV(JIG_UART_ON)] = {0x1d, "619K", &cfg_JIG_UART_ON,},
|
||||
/* 0x1e: Audio Mode with Remote */
|
||||
[MDEV(TA)] = {0x1f, "OPEN", &cfg_TA,},
|
||||
[MDEV(USB)] = {0x1f, "OPEN", &cfg_USB,},
|
||||
[MDEV(CDP)] = {0x1f, "OPEN", &cfg_CDP,},
|
||||
[MDEV(UNDEFINED_CHARGING)] = {0xfe, "UNDEFINED", &cfg_UNDEFINED_CHARGING,},
|
||||
[ATTACHED_DEV_NUM] = {0x00, "NUM", NULL,},
|
||||
};
|
||||
|
||||
struct vps_tbl_data * mdev_to_vps(muic_attached_dev_t mdev)
|
||||
{
|
||||
if (mdev >= ATTACHED_DEV_NUM) {
|
||||
pr_err("%s Out of range mdev=%d\n", __func__, mdev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &vps_table[mdev];
|
||||
}
|
||||
|
||||
bool vps_name_to_mdev(const char *name, int *sdev)
|
||||
{
|
||||
struct vps_tbl_data *pvps;
|
||||
int mdev;
|
||||
|
||||
for (mdev = MDEV(NONE); mdev < ATTACHED_DEV_NUM; mdev++) {
|
||||
pvps = &vps_table[mdev];
|
||||
if (!pvps->cfg)
|
||||
continue;
|
||||
|
||||
if (!strcmp(pvps->cfg->name, name)){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mdev >= ATTACHED_DEV_NUM) {
|
||||
pr_err("%s Out of range mdev=%d, %s\n", __func__,
|
||||
mdev, name);
|
||||
return false;
|
||||
}
|
||||
|
||||
pr_info("%s:%s->[%2d]\n", __func__, pvps->cfg->name, mdev);
|
||||
|
||||
*sdev = mdev;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vps_is_supported_dev(muic_attached_dev_t mdev)
|
||||
{
|
||||
struct vps_tbl_data *pvps = mdev_to_vps(mdev);
|
||||
int attr;
|
||||
|
||||
if (!pvps || !pvps->cfg)
|
||||
return false;
|
||||
|
||||
attr = pvps->cfg->attr;
|
||||
|
||||
if (MATTR_TO_SUPP(attr))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void vps_update_supported_attr(muic_attached_dev_t mdev, bool supported)
|
||||
{
|
||||
struct vps_tbl_data *pvps = mdev_to_vps(mdev);
|
||||
|
||||
if (!pvps || !pvps->cfg)
|
||||
return;
|
||||
|
||||
if (supported)
|
||||
pvps->cfg->attr |= MATTR_SUPP;
|
||||
else
|
||||
pvps->cfg->attr &= (~MATTR_SUPP) & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
bool vps_is_factory_dev(muic_attached_dev_t mdev)
|
||||
{
|
||||
struct vps_tbl_data *pvps = mdev_to_vps(mdev);
|
||||
int attr;
|
||||
|
||||
if (!pvps || !pvps->cfg)
|
||||
return false;
|
||||
|
||||
attr = pvps->cfg->attr;
|
||||
|
||||
if (MATTR_TO_FACT(attr))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool vps_is_1k_mhl_cable(vps_data_t *pmsr)
|
||||
{
|
||||
#define STATUS1_ADC1K_SHIFT 7
|
||||
u8 adc1k = 0x1 << STATUS1_ADC1K_SHIFT; /* VPS_VENDOR */
|
||||
|
||||
return (pmsr->t.adc1k == adc1k) ? true : false;
|
||||
}
|
||||
|
||||
static bool vps_is_adc(vps_data_t *pmsr, struct vps_tbl_data *pvps)
|
||||
{
|
||||
if (pmsr->t.adc == pvps->adc)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static bool vps_is_vbvolt(vps_data_t *pmsr, struct vps_tbl_data *pvps)
|
||||
{
|
||||
int attr = pvps->cfg->attr;
|
||||
|
||||
if (pmsr->t.vbvolt == MATTR_TO_VBUS(attr))
|
||||
return true;
|
||||
|
||||
if (MATTR_TO_VBUS(attr) == VB_ANY)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check it the resolved device type is treated as a different one
|
||||
* when VBUS also comes along.
|
||||
*/
|
||||
int resolve_twin_mdev(int mdev, bool vbus)
|
||||
{
|
||||
if (vbus) {
|
||||
if (mdev == MDEV(DESKDOCK)) {
|
||||
pr_info("%s: mdev:%d vbus:%d\n",__func__, mdev, vbus);
|
||||
return MDEV(DESKDOCK_VB);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int resolve_dev_based_on_adc_chgtype(muic_data_t *pmuic, vps_data_t *pmsr)
|
||||
{
|
||||
int dev_type;
|
||||
|
||||
pr_info("%s: adc=%02x, chgtyp=%02x\n",__func__, pmsr->t.adc, pmsr->t.chgtyp);
|
||||
|
||||
switch(pmsr->t.adc){
|
||||
case ADC_OPEN:
|
||||
if(pmsr->t.chgtyp == CHGTYP_DEDICATED_CHARGER)
|
||||
dev_type = ATTACHED_DEV_TA_MUIC;
|
||||
else if(pmsr->t.chgtyp == CHGTYP_UNOFFICIAL_CHARGER)
|
||||
dev_type = ATTACHED_DEV_TA_MUIC;
|
||||
else if(pmsr->t.chgtyp == CHGTYP_USB)
|
||||
dev_type= ATTACHED_DEV_USB_MUIC;
|
||||
else if(pmsr->t.chgtyp == CHGTYP_CDP)
|
||||
dev_type = ATTACHED_DEV_CDP_MUIC;
|
||||
else{
|
||||
pr_info("%s not able to resolve using ADC and CHGTYPE\n",__func__);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case ADC_219:
|
||||
if(pmsr->t.chgtyp == CHGTYP_DEDICATED_CHARGER)
|
||||
dev_type = ATTACHED_DEV_TA_MUIC;
|
||||
else if(pmsr->t.chgtyp == CHGTYP_UNOFFICIAL_CHARGER)
|
||||
dev_type = ATTACHED_DEV_TA_MUIC;
|
||||
else if(pmsr->t.chgtyp == CHGTYP_CDP)
|
||||
dev_type = ATTACHED_DEV_CDP_MUIC;
|
||||
else if(pmsr->t.chgtyp == CHGTYP_USB)
|
||||
dev_type = ATTACHED_DEV_USB_MUIC;
|
||||
else {
|
||||
pr_info("%s not able to resolve using ADC and CHGTYPE\n",__func__);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case ADC_SMARTDOCK:
|
||||
if(pmsr->t.chgtyp == CHGTYP_DEDICATED_CHARGER)
|
||||
dev_type = ATTACHED_DEV_SMARTDOCK_MUIC;
|
||||
else if(pmsr->t.chgtyp == CHGTYP_USB)
|
||||
dev_type = ATTACHED_DEV_SMARTDOCK_MUIC;
|
||||
else{
|
||||
pr_info("%s not able to resolve using ADC and CHGTYPE\n",__func__);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_info("%s not able to resolve using ADC and CHGTYPE\n",__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return dev_type;
|
||||
}
|
||||
|
||||
int vps_find_attached_dev(muic_data_t *pmuic, muic_attached_dev_t *pdev, int *pintr)
|
||||
{
|
||||
struct vps_tbl_data *pvps;
|
||||
muic_attached_dev_t new_dev = ATTACHED_DEV_UNKNOWN_MUIC, mdev;
|
||||
vps_data_t *pmsr = &pmuic->vps;
|
||||
int attr;
|
||||
int intr = MUIC_INTR_ATTACH;
|
||||
|
||||
pr_info("%s\n",__func__);
|
||||
|
||||
for (mdev = MDEV(NONE); mdev < ATTACHED_DEV_NUM; mdev++) {
|
||||
pvps = &vps_table[mdev];
|
||||
if (!pvps->cfg)
|
||||
continue;
|
||||
|
||||
attr = pvps->cfg->attr;
|
||||
|
||||
if (vps_is_1k_mhl_cable(pmsr)) {
|
||||
new_dev = mdev;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!vps_is_adc(pmsr, pvps))
|
||||
continue;
|
||||
|
||||
if (!vps_is_vbvolt(pmsr, pvps))
|
||||
continue;
|
||||
|
||||
if(MATTR_TO_CDET(attr)){
|
||||
/* some function for cdeten check */
|
||||
new_dev = resolve_dev_based_on_adc_chgtype(pmuic,pmsr);
|
||||
if(new_dev != -1)
|
||||
break;
|
||||
}
|
||||
|
||||
pr_info("%s:%s vps table match found at mdev:%d(%s)\n",
|
||||
MUIC_DEV_NAME, __func__, mdev, pvps->cfg->name);
|
||||
new_dev = mdev;
|
||||
break;
|
||||
}
|
||||
|
||||
if (mdev == ATTACHED_DEV_NUM) {
|
||||
if (pmsr->t.vbvolt == VB_HIGH) {
|
||||
new_dev = ATTACHED_DEV_UNDEFINED_CHARGING_MUIC;
|
||||
pr_info("%s:%s unsupported ID + VB\n", MUIC_DEV_NAME, __func__);
|
||||
}
|
||||
else
|
||||
{
|
||||
intr = MUIC_INTR_DETACH;
|
||||
new_dev = ATTACHED_DEV_NONE_MUIC;
|
||||
if(pvps->cfg)
|
||||
pr_info("%s:%s vps table match found at mdev:%d(%s)\n",
|
||||
MUIC_DEV_NAME, __func__, mdev, pvps->cfg->name);
|
||||
}
|
||||
}
|
||||
|
||||
*pintr = intr;
|
||||
*pdev = new_dev;
|
||||
|
||||
return new_dev;
|
||||
}
|
||||
|
||||
/*
|
||||
* vps_show_tablbe() functions displays the VPS table as a below format
|
||||
dev ADC (RID) attr vps_name
|
||||
--------------------------------------------------------------
|
||||
[ 1] = 1f( OPEN) 00000521:S:_ USB
|
||||
[ 2] = 1f( OPEN) 00000521:S:_ CDP
|
||||
[ 3] = 00( GND) 00000122:S:_ OTG
|
||||
[ 4] = 1f( OPEN) 00000521:S:_ TA
|
||||
[12] = fe( UNDEFINED) 00000121:S:_ Undefined Charging
|
||||
[13] = 1a( 365K) 00000122:S:_ Deskdock
|
||||
[16] = 1c( 523K) 00000333:S:F Jig UART Off
|
||||
[20] = 1d( 619K) 00000333:S:F Jig UART On
|
||||
[21] = 18( 255K) 00000351:S:F Jig USB Off
|
||||
[22] = 19( 301K) 00000351:S:F Jig USB On
|
||||
[23] = 10( 40.2K) 00000022:_:_ Smartdock
|
||||
[27] = 15( 121K) 00000151:S:_ Universal Multimedia dock
|
||||
[28] = 12( 64.9K) 00000051:_:_ Audiodock
|
||||
[29] = fe( 1K) 00000123:S:_ MHL
|
||||
[30] = 14( 102K) 00000153:S:_ Charging Cable
|
||||
[45] = 11( 49.9K) 00000053:_:_ HMT
|
||||
[46] = 0e( 28.7K) 00000023:_:_ VZW Accessory
|
||||
[47] = 0f( 34K) 00000023:_:_ VZW Incompatible
|
||||
[48] = 13( 80.07K) 00000123:S:_ USB LANHUB
|
||||
[49] = 1b( 442K) 00000123:S:_ TYPE2 Charger
|
||||
*/
|
||||
void vps_show_table(void)
|
||||
{
|
||||
struct vps_tbl_data *pvps;
|
||||
int mdev, attr;
|
||||
|
||||
pr_info(" %4s%6s%10s %12s %29s\n", "dev", "ADC", "(RID)", "attr", "vps_name");
|
||||
for (mdev = MDEV(NONE); mdev < ATTACHED_DEV_NUM; mdev++) {
|
||||
|
||||
pvps = &vps_table[mdev];
|
||||
|
||||
if (!pvps->cfg)
|
||||
continue;
|
||||
|
||||
attr = pvps->cfg->attr;
|
||||
|
||||
pr_info(" [%2d] = %02x(%10s) %08x:%c:%c %28s\n", mdev,
|
||||
pvps->adc, pvps->rid, pvps->cfg->attr,
|
||||
MATTR_TO_SUPP(attr) ? 'S' : '_',
|
||||
MATTR_TO_FACT(attr) ? 'F' : '_',
|
||||
pvps->cfg->name);
|
||||
}
|
||||
|
||||
pr_info("done.\n");
|
||||
|
||||
}
|
||||
|
||||
void vps_show_supported_list(void)
|
||||
{
|
||||
struct vps_tbl_data *pvps;
|
||||
int mdev, attr;
|
||||
|
||||
pr_info(" %4s%6s%10s %12s %30s\n", "dev", "ADC", "(RID)", "attr", "vps_name");
|
||||
for (mdev = MDEV(NONE); mdev < ATTACHED_DEV_NUM; mdev++) {
|
||||
|
||||
pvps = &vps_table[mdev];
|
||||
|
||||
if (!pvps->cfg)
|
||||
continue;
|
||||
|
||||
attr = pvps->cfg->attr;
|
||||
|
||||
if (!MATTR_TO_SUPP(attr))
|
||||
continue;
|
||||
|
||||
pr_info(" [%2d] = %02x(%10s) %08x:%c:%c %28s\n", mdev,
|
||||
pvps->adc, pvps->rid, pvps->cfg->attr,
|
||||
MATTR_TO_SUPP(attr) ? 'S' : '_',
|
||||
MATTR_TO_FACT(attr) ? 'F' : '_',
|
||||
pvps->cfg->name);
|
||||
|
||||
}
|
||||
|
||||
pr_info("done.\n");
|
||||
|
||||
}
|
||||
|
||||
static int resolve_dedicated_dev(muic_data_t *pmuic, muic_attached_dev_t *pdev, int *pintr)
|
||||
{
|
||||
muic_attached_dev_t new_dev = ATTACHED_DEV_UNKNOWN_MUIC;
|
||||
int intr = MUIC_INTR_DETACH;
|
||||
int vbvolt = 0, adc = 0;
|
||||
int twin_mdev = 0;
|
||||
|
||||
#if defined(CONFIG_MUIC_UNIVERSAL_SM5504)
|
||||
new_dev = pmuic->vps.t.attached_dev;
|
||||
if (new_dev != ATTACHED_DEV_UNKNOWN_MUIC)
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
adc = pmuic->vps.s.adc;
|
||||
vbvolt = pmuic->vps.s.vbvolt;
|
||||
#else
|
||||
int val1, val2, val3;
|
||||
|
||||
val1 = pmuic->vps.s.val1;
|
||||
val2 = pmuic->vps.s.val2;
|
||||
val3 = pmuic->vps.s.val3;
|
||||
adc = pmuic->vps.s.adc;
|
||||
vbvolt = pmuic->vps.s.vbvolt;
|
||||
|
||||
/* Attached */
|
||||
switch (val1) {
|
||||
case DEV_TYPE1_CDP:
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_CDP_MUIC;
|
||||
pr_info("%s : USB_CDP DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
case DEV_TYPE1_USB:
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_USB_MUIC;
|
||||
pr_info("%s : USB DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
case DEV_TYPE1_DEDICATED_CHG:
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_TA_MUIC;
|
||||
pr_info("%s : DEDICATED CHARGER DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
case DEV_TYPE1_USB_OTG:
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_OTG_MUIC;
|
||||
pr_info("%s : USB_OTG DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
case DEV_TYPE1_AUDIO_2:
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_USB_LANHUB_MUIC;
|
||||
pr_info("%s : LANHUB DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (val2) {
|
||||
case DEV_TYPE2_JIG_UART_OFF:
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_JIG_UART_OFF_MUIC;
|
||||
pr_info("%s : JIG_UART_OFF DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
case DEV_TYPE2_JIG_USB_OFF:
|
||||
if (!vbvolt) break;
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_JIG_USB_OFF_MUIC;
|
||||
pr_info("%s : JIG_USB_OFF DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
case DEV_TYPE2_JIG_USB_ON:
|
||||
if (!vbvolt) break;
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_JIG_USB_ON_MUIC;
|
||||
pr_info("%s : JIG_USB_ON DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
case DEV_TYPE2_TTY:
|
||||
if (!vbvolt) break;
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_UNIVERSAL_MMDOCK_MUIC;
|
||||
pr_info("%s : UNIVERSAL_MMDOCK DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (val3 & DEV_TYPE3_CHG_TYPE)
|
||||
{
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
|
||||
if (val3 & DEV_TYPE3_NO_STD_CHG) {
|
||||
new_dev = ATTACHED_DEV_USB_MUIC;
|
||||
pr_info("%s : TYPE3 DCD_OUT_TIMEOUT DETECTED\n", MUIC_DEV_NAME);
|
||||
|
||||
} else {
|
||||
new_dev = ATTACHED_DEV_TA_MUIC;
|
||||
pr_info("%s : TYPE3_CHARGER DETECTED\n", MUIC_DEV_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
if (val2 & DEV_TYPE2_AV || val3 & DEV_TYPE3_AV_WITH_VBUS)
|
||||
{
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_DESKDOCK_MUIC;
|
||||
pr_info("%s : DESKDOCK DETECTED\n", MUIC_DEV_NAME);
|
||||
}
|
||||
|
||||
if (val3 & DEV_TYPE3_MHL)
|
||||
{
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_MHL_MUIC;
|
||||
pr_info("%s : MHL DETECTED\n", MUIC_DEV_NAME);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If there is no matching device found using device type registers
|
||||
use ADC to find the attached device */
|
||||
if(new_dev == ATTACHED_DEV_UNKNOWN_MUIC) {
|
||||
switch (adc) {
|
||||
case ADC_USB_LANHUB:
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_USB_LANHUB_MUIC;
|
||||
pr_info("%s : LANHUB DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
|
||||
case ADC_CEA936ATYPE1_CHG : /*200k ohm */
|
||||
{
|
||||
int rescanned_dev;
|
||||
|
||||
if (!vbvolt)
|
||||
break;
|
||||
|
||||
/* For LG USB cable which has 219k ohm ID */
|
||||
rescanned_dev = do_BCD_rescan(pmuic);
|
||||
|
||||
if (rescanned_dev > 0) {
|
||||
pr_info("%s : TYPE1 CHARGER DETECTED(USB)\n", MUIC_DEV_NAME);
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = rescanned_dev;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ADC_CEA936ATYPE2_CHG:
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_TA_MUIC;
|
||||
pr_info("%s : TYPE1/2 CHARGER DETECTED(TA)\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
case ADC_JIG_USB_OFF: /* 255k */
|
||||
if (!vbvolt) break;
|
||||
if (new_dev != ATTACHED_DEV_JIG_USB_OFF_MUIC) {
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_JIG_USB_OFF_MUIC;
|
||||
pr_info("%s : ADC JIG_USB_OFF DETECTED\n", MUIC_DEV_NAME);
|
||||
}
|
||||
break;
|
||||
case ADC_JIG_USB_ON:
|
||||
if (!vbvolt) break;
|
||||
if (new_dev != ATTACHED_DEV_JIG_USB_ON_MUIC) {
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_JIG_USB_ON_MUIC;
|
||||
pr_info("%s : ADC JIG_USB_ON DETECTED\n", MUIC_DEV_NAME);
|
||||
}
|
||||
break;
|
||||
case ADC_JIG_UART_OFF:
|
||||
if (new_dev != ATTACHED_DEV_JIG_UART_OFF_MUIC) {
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_JIG_UART_OFF_MUIC;
|
||||
pr_info("%s : ADC JIG_UART_OFF DETECTED\n", MUIC_DEV_NAME);
|
||||
}
|
||||
break;
|
||||
case ADC_JIG_UART_ON:
|
||||
/* This is the mode to wake up device during factory mode.
|
||||
* This device type SHOULD be handled in muic_state.c to
|
||||
* support both factory & rustproof mode.
|
||||
*/
|
||||
if (new_dev != ATTACHED_DEV_JIG_UART_ON_MUIC) {
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_JIG_UART_ON_MUIC;
|
||||
pr_info("%s : ADC JIG_UART_ON DETECTED\n", MUIC_DEV_NAME);
|
||||
}
|
||||
break;
|
||||
#ifdef CONFIG_MUIC_SM5703_SUPPORT_AUDIODOCK
|
||||
case ADC_AUDIODOCK:
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_AUDIODOCK_MUIC;
|
||||
pr_info("%s : ADC AUDIODOCK DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
#endif
|
||||
case ADC_CHARGING_CABLE:
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_CHARGING_CABLE_MUIC;
|
||||
pr_info("%s : PS_CABLE DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
case ADC_DESKDOCK:
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_DESKDOCK_MUIC;
|
||||
pr_info("%s : ADC DESKDOCK DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
case ADC_OPEN:
|
||||
/* sometimes muic fails to catch JIG_UART_OFF detaching */
|
||||
/* double check with ADC */
|
||||
if (new_dev == ATTACHED_DEV_JIG_UART_OFF_MUIC) {
|
||||
new_dev = ATTACHED_DEV_UNKNOWN_MUIC;
|
||||
intr = MUIC_INTR_DETACH;
|
||||
pr_info("%s : ADC OPEN DETECTED\n", MUIC_DEV_NAME);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (pmuic->attached_dev == ATTACHED_DEV_NONE_MUIC) {
|
||||
/* Workaround for Noise test */
|
||||
if(vbvolt) {
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_UNDEFINED_CHARGING_MUIC;
|
||||
pr_info("%s : UNDEFINED VB DETECTED\n", MUIC_DEV_NAME);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ADC_UNIVERSAL_MMDOCK:
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_UNIVERSAL_MMDOCK_MUIC;
|
||||
pr_info("%s : ADC UNIVERSAL_MMDOCK\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
|
||||
case ADC_RESERVED_VZW:
|
||||
new_dev = ATTACHED_DEV_VZW_ACC_MUIC;
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
pr_info("%s : ADC VZW_ACC DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
|
||||
case ADC_INCOMPATIBLE_VZW:
|
||||
new_dev = ATTACHED_DEV_VZW_INCOMPATIBLE_MUIC;
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
pr_info("%s : ADC INCOMPATIBLE_VZW DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
|
||||
case ADC_HMT:
|
||||
new_dev = ATTACHED_DEV_HMT_MUIC;
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
pr_info("%s : ADC HMT DETECTED\n", MUIC_DEV_NAME);
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_warn("%s:%s unsupported ADC(0x%02x)\n", MUIC_DEV_NAME,
|
||||
__func__, adc);
|
||||
if(vbvolt) {
|
||||
intr = MUIC_INTR_ATTACH;
|
||||
new_dev = ATTACHED_DEV_UNDEFINED_CHARGING_MUIC;
|
||||
pr_info("%s : UNDEFINED VB DETECTED\n", MUIC_DEV_NAME);
|
||||
} else
|
||||
intr = MUIC_INTR_DETACH;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check it the cable type is supported.
|
||||
*/
|
||||
|
||||
if (vps_is_supported_dev(new_dev)) {
|
||||
if ((twin_mdev = resolve_twin_mdev(new_dev, vbvolt))) {
|
||||
new_dev = twin_mdev;
|
||||
pr_info("%s:Supported twin mdev-> %d\n", __func__, twin_mdev);
|
||||
} else
|
||||
pr_info("%s:Supported.\n", __func__);
|
||||
} else if(vbvolt && (intr == MUIC_INTR_ATTACH)) {
|
||||
new_dev = ATTACHED_DEV_UNDEFINED_CHARGING_MUIC;
|
||||
pr_info("%s:Unsupported->UNDEFINED_CHARGING\n", __func__);
|
||||
} else {
|
||||
intr = MUIC_INTR_DETACH;
|
||||
new_dev = ATTACHED_DEV_UNKNOWN_MUIC;
|
||||
pr_info("%s:Unsupported->Discarded.\n", __func__);
|
||||
}
|
||||
|
||||
*pintr = intr;
|
||||
*pdev = new_dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vps_resolve_dev(muic_data_t *pmuic, muic_attached_dev_t *pdev, int *pintr)
|
||||
{
|
||||
if(pmuic->vps_table == VPS_TYPE_TABLE)
|
||||
return vps_find_attached_dev(pmuic,pdev,pintr);
|
||||
else
|
||||
return resolve_dedicated_dev(pmuic, pdev, pintr);
|
||||
}
|
106
drivers/muic/universal/muic_vps.h
Normal file
106
drivers/muic/universal/muic_vps.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
#ifndef _MUIC_VPS_H_
|
||||
#define _MUIC_VPS_H_
|
||||
|
||||
/* MUIC Output of USB Charger Detection */
|
||||
typedef enum {
|
||||
/* No Valid voltage at VB (Vvb < Vvbdet) */
|
||||
CHGTYP_NO_VOLTAGE = 0x00,
|
||||
/* Unknown (D+/D- does not present a valid USB charger signature) */
|
||||
CHGTYP_USB = 0x01,
|
||||
/* Charging Downstream Port */
|
||||
CHGTYP_CDP = 0x02,
|
||||
/* Dedicated Charger (D+/D- shorted) */
|
||||
CHGTYP_DEDICATED_CHARGER = 0x03,
|
||||
/* Special 500mA charger, max current 500mA */
|
||||
CHGTYP_500MA = 0x04,
|
||||
/* Special 1A charger, max current 1A */
|
||||
CHGTYP_1A = 0x05,
|
||||
/* Special charger - 3.3V bias on D+/D- */
|
||||
CHGTYP_SPECIAL_3_3V_CHARGER = 0x06,
|
||||
/* Reserved */
|
||||
CHGTYP_RFU = 0x07,
|
||||
/* Any charger w/o USB */
|
||||
CHGTYP_UNOFFICIAL_CHARGER = 0xfc,
|
||||
/* Any charger type */
|
||||
CHGTYP_ANY = 0xfd,
|
||||
/* Don't care charger type */
|
||||
CHGTYP_DONTCARE = 0xfe,
|
||||
|
||||
CHGTYP_MAX,
|
||||
|
||||
CHGTYP_INIT,
|
||||
CHGTYP_MIN = CHGTYP_NO_VOLTAGE
|
||||
} chgtyp_t;
|
||||
|
||||
#define MDEV(name) ATTACHED_DEV_##name##_MUIC
|
||||
/*
|
||||
* VPS attribute field.
|
||||
|
||||
b'xxxxxxxx_xxxxxxx_xxxx_xxfs_cccc_vvvv
|
||||
x: undefined
|
||||
f: factory device
|
||||
s: supported
|
||||
c: com port
|
||||
v: vbus
|
||||
*/
|
||||
#define VPS_CHGDET_BITN 10
|
||||
#define VPS_FAC_BITN 9
|
||||
#define VPS_SUP_BITN 8
|
||||
#define VPS_COM_BITN 4
|
||||
#define VPS_VBUS_BITN 0
|
||||
#define VPS_CHGDET_MASK 0x1
|
||||
#define VPS_FAC_MASK 0x1
|
||||
#define VPS_SUP_MASK 0x1
|
||||
#define VPS_COM_MASK 0xF
|
||||
#define VPS_VBUS_MASK 0xF
|
||||
|
||||
#define MATTR(com,vbus) \
|
||||
(com << VPS_COM_BITN) | \
|
||||
(vbus << VPS_VBUS_BITN)
|
||||
|
||||
#define MATTR_TO_VBUS(a) ((a >> VPS_VBUS_BITN) & VPS_VBUS_MASK)
|
||||
#define MATTR_TO_COM(a) ((a >> VPS_COM_BITN) & VPS_COM_MASK)
|
||||
#define MATTR_TO_FACT(a) ((a >> VPS_FAC_BITN) & VPS_FAC_MASK)
|
||||
#define MATTR_TO_SUPP(a) ((a >> VPS_SUP_BITN) & VPS_SUP_MASK)
|
||||
#define MATTR_TO_CDET(a) ((a >> VPS_CHGDET_BITN) & VPS_CHGDET_MASK)
|
||||
#define MATTR_CDET (1 << VPS_CHGDET_BITN)
|
||||
#define MATTR_SUPP (1 << VPS_SUP_BITN)
|
||||
#define MATTR_FACT (1 << VPS_FAC_BITN)
|
||||
#define MATTR_CDET_SUPP ((1 << VPS_CHGDET_BITN) | MATTR_SUPP)
|
||||
#define MATTR_FACT_SUPP ((1 << VPS_FAC_BITN) | MATTR_SUPP)
|
||||
|
||||
enum vps_vbvolt{
|
||||
VB_LOW = 0,
|
||||
VB_HIGH = 1,
|
||||
VB_CHK = 2,
|
||||
VB_ANY = 3,
|
||||
};
|
||||
|
||||
enum vps_com{
|
||||
VCOM_OPEN = COM_OPEN_WITH_V_BUS,
|
||||
VCOM_USB = COM_USB_AP,
|
||||
VCOM_AUDIO = COM_AUDIO,
|
||||
VCOM_UART = COM_UART_AP,
|
||||
VCOM_USB_CP = COM_USB_CP,
|
||||
VCOM_UART_CP = COM_UART_CP,
|
||||
};
|
||||
|
||||
struct vps_cfg {
|
||||
char *name;
|
||||
int attr;
|
||||
};
|
||||
|
||||
struct vps_tbl_data {
|
||||
u8 adc;
|
||||
char *rid;
|
||||
struct vps_cfg *cfg;
|
||||
};
|
||||
extern bool vps_name_to_mdev(const char *name, int *sdev);
|
||||
extern void vps_update_supported_attr(muic_attached_dev_t mdev, bool supported);
|
||||
extern bool vps_is_supported_dev(muic_attached_dev_t mdev);
|
||||
extern int vps_find_attached_dev(muic_data_t *pmuic, muic_attached_dev_t *pdev, int *pintr);
|
||||
extern void vps_show_table(void);
|
||||
extern void vps_show_supported_list(void);
|
||||
extern int vps_resolve_dev(muic_data_t *pmuic, muic_attached_dev_t *pbuf, int *pintr);
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue