Commit 6a57bad6 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'extcon-next-for-3.16' of...

Merge tag 'extcon-next-for-3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/extcon into char-misc-next

Chanwoo writes:

Update extcon for v3.16

This patchset add resource-managed functions to automatically control the memory
and unregistration operation of extcon. Also, This series support new MAX77836
extcon device driver on existing MAX14577 device because existed a little
difference between MAX77836 and MAX14577. Finally, Fix minor issue of extcon
driver.

Detailed description for patchset:
1. Add resource-managed functions
- Add resource-managed functions to automatically free the memory of extcon
structure and to control unregistration behavior as following. This new devm_*
functions applied all of extcon drivers in drivers/extcon/.
: devm_extcon_dev_register/unregister()
: devm_extcon_dev_allocate/free()
: extcon_dev_allocate/free() for devm_extcon_dev_allocate/free()

2. Add new MAX77836 extcon device
- Support MAX77836 device on existing MAX14577 device driver using
different compatible string. This patchset has dependency on MFD/
Regulator/Extcon. So, Lee Jones(MFD Maintainer) created Immutable
branch between MFD and Extcon due for v3.16 merge-window and then
I merged this patchset from MFD git repo[1] to Extcon git repo.
: [1] git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
  (branch: ib-mfd-extcon-3.16)

3. Fix minor issue of extcon driver
- extcon-palmas driver
: Fix issue of extcon device name for probe
- extcon-max14577
: Fix probe failure about handling wrong return value.
: Properly Handle return value of regmap_irq_get_virq function.
- extcon-max8997/max77693 driver
: Fix NULL pointer exception on missing pdata

4. Code clean for extcon driver
- extcon-max8997/max77693
: Use power efficient workqueue for delayed cable detection
parents 4f063810 3f79a3fb
...@@ -28,13 +28,13 @@ config EXTCON_ADC_JACK ...@@ -28,13 +28,13 @@ config EXTCON_ADC_JACK
Say Y here to enable extcon device driver based on ADC values. Say Y here to enable extcon device driver based on ADC values.
config EXTCON_MAX14577 config EXTCON_MAX14577
tristate "MAX14577 EXTCON Support" tristate "MAX14577/77836 EXTCON Support"
depends on MFD_MAX14577 depends on MFD_MAX14577
select IRQ_DOMAIN select IRQ_DOMAIN
select REGMAP_I2C select REGMAP_I2C
help help
If you say yes here you get support for the MUIC device of If you say yes here you get support for the MUIC device of
Maxim MAX14577 PMIC. The MAX14577 MUIC is a USB port accessory Maxim MAX14577/77836. The MAX14577/77836 MUIC is a USB port accessory
detector and switch. detector and switch.
config EXTCON_MAX77693 config EXTCON_MAX77693
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
* @chan: iio channel being queried. * @chan: iio channel being queried.
*/ */
struct adc_jack_data { struct adc_jack_data {
struct extcon_dev edev; struct extcon_dev *edev;
const char **cable_names; const char **cable_names;
int num_cables; int num_cables;
...@@ -64,7 +64,7 @@ static void adc_jack_handler(struct work_struct *work) ...@@ -64,7 +64,7 @@ static void adc_jack_handler(struct work_struct *work)
ret = iio_read_channel_raw(data->chan, &adc_val); ret = iio_read_channel_raw(data->chan, &adc_val);
if (ret < 0) { if (ret < 0) {
dev_err(&data->edev.dev, "read channel() error: %d\n", ret); dev_err(&data->edev->dev, "read channel() error: %d\n", ret);
return; return;
} }
...@@ -80,7 +80,7 @@ static void adc_jack_handler(struct work_struct *work) ...@@ -80,7 +80,7 @@ static void adc_jack_handler(struct work_struct *work)
} }
/* if no def has met, it means state = 0 (no cables attached) */ /* if no def has met, it means state = 0 (no cables attached) */
extcon_set_state(&data->edev, state); extcon_set_state(data->edev, state);
} }
static irqreturn_t adc_jack_irq_thread(int irq, void *_data) static irqreturn_t adc_jack_irq_thread(int irq, void *_data)
...@@ -102,33 +102,33 @@ static int adc_jack_probe(struct platform_device *pdev) ...@@ -102,33 +102,33 @@ static int adc_jack_probe(struct platform_device *pdev)
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
data->edev.name = pdata->name;
if (!pdata->cable_names) { if (!pdata->cable_names) {
err = -EINVAL;
dev_err(&pdev->dev, "error: cable_names not defined.\n"); dev_err(&pdev->dev, "error: cable_names not defined.\n");
goto out; return -EINVAL;
} }
data->edev.dev.parent = &pdev->dev; data->edev = devm_extcon_dev_allocate(&pdev->dev, pdata->cable_names);
data->edev.supported_cable = pdata->cable_names; if (IS_ERR(data->edev)) {
dev_err(&pdev->dev, "failed to allocate extcon device\n");
return -ENOMEM;
}
data->edev->dev.parent = &pdev->dev;
data->edev->name = pdata->name;
/* Check the length of array and set num_cables */ /* Check the length of array and set num_cables */
for (i = 0; data->edev.supported_cable[i]; i++) for (i = 0; data->edev->supported_cable[i]; i++)
; ;
if (i == 0 || i > SUPPORTED_CABLE_MAX) { if (i == 0 || i > SUPPORTED_CABLE_MAX) {
err = -EINVAL;
dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n", dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n",
i - 1); i - 1);
goto out; return -EINVAL;
} }
data->num_cables = i; data->num_cables = i;
if (!pdata->adc_conditions || if (!pdata->adc_conditions ||
!pdata->adc_conditions[0].state) { !pdata->adc_conditions[0].state) {
err = -EINVAL;
dev_err(&pdev->dev, "error: adc_conditions not defined.\n"); dev_err(&pdev->dev, "error: adc_conditions not defined.\n");
goto out; return -EINVAL;
} }
data->adc_conditions = pdata->adc_conditions; data->adc_conditions = pdata->adc_conditions;
...@@ -138,10 +138,8 @@ static int adc_jack_probe(struct platform_device *pdev) ...@@ -138,10 +138,8 @@ static int adc_jack_probe(struct platform_device *pdev)
data->num_conditions = i; data->num_conditions = i;
data->chan = iio_channel_get(&pdev->dev, pdata->consumer_channel); data->chan = iio_channel_get(&pdev->dev, pdata->consumer_channel);
if (IS_ERR(data->chan)) { if (IS_ERR(data->chan))
err = PTR_ERR(data->chan); return PTR_ERR(data->chan);
goto out;
}
data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms); data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms);
...@@ -149,15 +147,14 @@ static int adc_jack_probe(struct platform_device *pdev) ...@@ -149,15 +147,14 @@ static int adc_jack_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data); platform_set_drvdata(pdev, data);
err = extcon_dev_register(&data->edev); err = devm_extcon_dev_register(&pdev->dev, data->edev);
if (err) if (err)
goto out; return err;
data->irq = platform_get_irq(pdev, 0); data->irq = platform_get_irq(pdev, 0);
if (!data->irq) { if (!data->irq) {
dev_err(&pdev->dev, "platform_get_irq failed\n"); dev_err(&pdev->dev, "platform_get_irq failed\n");
err = -ENODEV; return -ENODEV;
goto err_irq;
} }
err = request_any_context_irq(data->irq, adc_jack_irq_thread, err = request_any_context_irq(data->irq, adc_jack_irq_thread,
...@@ -165,15 +162,10 @@ static int adc_jack_probe(struct platform_device *pdev) ...@@ -165,15 +162,10 @@ static int adc_jack_probe(struct platform_device *pdev)
if (err < 0) { if (err < 0) {
dev_err(&pdev->dev, "error: irq %d\n", data->irq); dev_err(&pdev->dev, "error: irq %d\n", data->irq);
goto err_irq; return err;
} }
return 0; return 0;
err_irq:
extcon_dev_unregister(&data->edev);
out:
return err;
} }
static int adc_jack_remove(struct platform_device *pdev) static int adc_jack_remove(struct platform_device *pdev)
...@@ -182,7 +174,6 @@ static int adc_jack_remove(struct platform_device *pdev) ...@@ -182,7 +174,6 @@ static int adc_jack_remove(struct platform_device *pdev)
free_irq(data->irq, data); free_irq(data->irq, data);
cancel_work_sync(&data->handler.work); cancel_work_sync(&data->handler.work);
extcon_dev_unregister(&data->edev);
return 0; return 0;
} }
......
...@@ -91,7 +91,7 @@ struct arizona_extcon_info { ...@@ -91,7 +91,7 @@ struct arizona_extcon_info {
int hpdet_ip; int hpdet_ip;
struct extcon_dev edev; struct extcon_dev *edev;
}; };
static const struct arizona_micd_config micd_default_modes[] = { static const struct arizona_micd_config micd_default_modes[] = {
...@@ -546,7 +546,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) ...@@ -546,7 +546,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
} }
/* If the cable was removed while measuring ignore the result */ /* If the cable was removed while measuring ignore the result */
ret = extcon_get_cable_state_(&info->edev, ARIZONA_CABLE_MECHANICAL); ret = extcon_get_cable_state_(info->edev, ARIZONA_CABLE_MECHANICAL);
if (ret < 0) { if (ret < 0) {
dev_err(arizona->dev, "Failed to check cable state: %d\n", dev_err(arizona->dev, "Failed to check cable state: %d\n",
ret); ret);
...@@ -581,7 +581,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) ...@@ -581,7 +581,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
else else
report = ARIZONA_CABLE_HEADPHONE; report = ARIZONA_CABLE_HEADPHONE;
ret = extcon_set_cable_state_(&info->edev, report, true); ret = extcon_set_cable_state_(info->edev, report, true);
if (ret != 0) if (ret != 0)
dev_err(arizona->dev, "Failed to report HP/line: %d\n", dev_err(arizona->dev, "Failed to report HP/line: %d\n",
ret); ret);
...@@ -664,7 +664,7 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info) ...@@ -664,7 +664,7 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
/* Just report headphone */ /* Just report headphone */
ret = extcon_update_state(&info->edev, ret = extcon_update_state(info->edev,
1 << ARIZONA_CABLE_HEADPHONE, 1 << ARIZONA_CABLE_HEADPHONE,
1 << ARIZONA_CABLE_HEADPHONE); 1 << ARIZONA_CABLE_HEADPHONE);
if (ret != 0) if (ret != 0)
...@@ -723,7 +723,7 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info) ...@@ -723,7 +723,7 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
/* Just report headphone */ /* Just report headphone */
ret = extcon_update_state(&info->edev, ret = extcon_update_state(info->edev,
1 << ARIZONA_CABLE_HEADPHONE, 1 << ARIZONA_CABLE_HEADPHONE,
1 << ARIZONA_CABLE_HEADPHONE); 1 << ARIZONA_CABLE_HEADPHONE);
if (ret != 0) if (ret != 0)
...@@ -764,7 +764,7 @@ static void arizona_micd_detect(struct work_struct *work) ...@@ -764,7 +764,7 @@ static void arizona_micd_detect(struct work_struct *work)
mutex_lock(&info->lock); mutex_lock(&info->lock);
/* If the cable was removed while measuring ignore the result */ /* If the cable was removed while measuring ignore the result */
ret = extcon_get_cable_state_(&info->edev, ARIZONA_CABLE_MECHANICAL); ret = extcon_get_cable_state_(info->edev, ARIZONA_CABLE_MECHANICAL);
if (ret < 0) { if (ret < 0) {
dev_err(arizona->dev, "Failed to check cable state: %d\n", dev_err(arizona->dev, "Failed to check cable state: %d\n",
ret); ret);
...@@ -812,7 +812,7 @@ static void arizona_micd_detect(struct work_struct *work) ...@@ -812,7 +812,7 @@ static void arizona_micd_detect(struct work_struct *work)
if (info->detecting && (val & ARIZONA_MICD_LVL_8)) { if (info->detecting && (val & ARIZONA_MICD_LVL_8)) {
arizona_identify_headphone(info); arizona_identify_headphone(info);
ret = extcon_update_state(&info->edev, ret = extcon_update_state(info->edev,
1 << ARIZONA_CABLE_MICROPHONE, 1 << ARIZONA_CABLE_MICROPHONE,
1 << ARIZONA_CABLE_MICROPHONE); 1 << ARIZONA_CABLE_MICROPHONE);
...@@ -999,7 +999,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data) ...@@ -999,7 +999,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
if (info->last_jackdet == present) { if (info->last_jackdet == present) {
dev_dbg(arizona->dev, "Detected jack\n"); dev_dbg(arizona->dev, "Detected jack\n");
ret = extcon_set_cable_state_(&info->edev, ret = extcon_set_cable_state_(info->edev,
ARIZONA_CABLE_MECHANICAL, true); ARIZONA_CABLE_MECHANICAL, true);
if (ret != 0) if (ret != 0)
...@@ -1038,7 +1038,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data) ...@@ -1038,7 +1038,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
info->micd_ranges[i].key, 0); info->micd_ranges[i].key, 0);
input_sync(info->input); input_sync(info->input);
ret = extcon_update_state(&info->edev, 0xffffffff, 0); ret = extcon_update_state(info->edev, 0xffffffff, 0);
if (ret != 0) if (ret != 0)
dev_err(arizona->dev, "Removal report failed: %d\n", dev_err(arizona->dev, "Removal report failed: %d\n",
ret); ret);
...@@ -1105,15 +1105,14 @@ static int arizona_extcon_probe(struct platform_device *pdev) ...@@ -1105,15 +1105,14 @@ static int arizona_extcon_probe(struct platform_device *pdev)
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info) { if (!info) {
dev_err(&pdev->dev, "Failed to allocate memory\n"); dev_err(&pdev->dev, "Failed to allocate memory\n");
ret = -ENOMEM; return -ENOMEM;
goto err;
} }
info->micvdd = devm_regulator_get(arizona->dev, "MICVDD"); info->micvdd = devm_regulator_get(arizona->dev, "MICVDD");
if (IS_ERR(info->micvdd)) { if (IS_ERR(info->micvdd)) {
ret = PTR_ERR(info->micvdd); ret = PTR_ERR(info->micvdd);
dev_err(arizona->dev, "Failed to get MICVDD: %d\n", ret); dev_err(arizona->dev, "Failed to get MICVDD: %d\n", ret);
goto err; return ret;
} }
mutex_init(&info->lock); mutex_init(&info->lock);
...@@ -1151,15 +1150,19 @@ static int arizona_extcon_probe(struct platform_device *pdev) ...@@ -1151,15 +1150,19 @@ static int arizona_extcon_probe(struct platform_device *pdev)
break; break;
} }
info->edev.name = "Headset Jack"; info->edev = devm_extcon_dev_allocate(&pdev->dev, arizona_cable);
info->edev.dev.parent = arizona->dev; if (IS_ERR(info->edev)) {
info->edev.supported_cable = arizona_cable; dev_err(&pdev->dev, "failed to allocate extcon device\n");
return -ENOMEM;
}
info->edev->name = "Headset Jack";
info->edev->dev.parent = arizona->dev;
ret = extcon_dev_register(&info->edev); ret = devm_extcon_dev_register(&pdev->dev, info->edev);
if (ret < 0) { if (ret < 0) {
dev_err(arizona->dev, "extcon_dev_register() failed: %d\n", dev_err(arizona->dev, "extcon_dev_register() failed: %d\n",
ret); ret);
goto err; return ret;
} }
info->input = devm_input_allocate_device(&pdev->dev); info->input = devm_input_allocate_device(&pdev->dev);
...@@ -1410,8 +1413,6 @@ static int arizona_extcon_probe(struct platform_device *pdev) ...@@ -1410,8 +1413,6 @@ static int arizona_extcon_probe(struct platform_device *pdev)
err_input: err_input:
err_register: err_register:
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
extcon_dev_unregister(&info->edev);
err:
return ret; return ret;
} }
...@@ -1445,7 +1446,6 @@ static int arizona_extcon_remove(struct platform_device *pdev) ...@@ -1445,7 +1446,6 @@ static int arizona_extcon_remove(struct platform_device *pdev)
regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE, regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
ARIZONA_JD1_ENA, 0); ARIZONA_JD1_ENA, 0);
arizona_clk32k_disable(arizona); arizona_clk32k_disable(arizona);
extcon_dev_unregister(&info->edev);
return 0; return 0;
} }
......
...@@ -565,6 +565,100 @@ static void dummy_sysfs_dev_release(struct device *dev) ...@@ -565,6 +565,100 @@ static void dummy_sysfs_dev_release(struct device *dev)
{ {
} }
/*
* extcon_dev_allocate() - Allocate the memory of extcon device.
* @supported_cable: Array of supported cable names ending with NULL.
* If supported_cable is NULL, cable name related APIs
* are disabled.
*
* This function allocates the memory for extcon device without allocating
* memory in each extcon provider driver and initialize default setting for
* extcon device.
*
* Return the pointer of extcon device if success or ERR_PTR(err) if fail
*/
struct extcon_dev *extcon_dev_allocate(const char **supported_cable)
{
struct extcon_dev *edev;
edev = kzalloc(sizeof(*edev), GFP_KERNEL);
if (!edev)
return ERR_PTR(-ENOMEM);
edev->max_supported = 0;
edev->supported_cable = supported_cable;
return edev;
}
/*
* extcon_dev_free() - Free the memory of extcon device.
* @edev: the extcon device to free
*/
void extcon_dev_free(struct extcon_dev *edev)
{
kfree(edev);
}
EXPORT_SYMBOL_GPL(extcon_dev_free);
static int devm_extcon_dev_match(struct device *dev, void *res, void *data)
{
struct extcon_dev **r = res;
if (WARN_ON(!r || !*r))
return 0;
return *r == data;
}
static void devm_extcon_dev_release(struct device *dev, void *res)
{
extcon_dev_free(*(struct extcon_dev **)res);
}
/**
* devm_extcon_dev_allocate - Allocate managed extcon device
* @dev: device owning the extcon device being created
* @supported_cable: Array of supported cable names ending with NULL.
* If supported_cable is NULL, cable name related APIs
* are disabled.
*
* This function manages automatically the memory of extcon device using device
* resource management and simplify the control of freeing the memory of extcon
* device.
*
* Returns the pointer memory of allocated extcon_dev if success
* or ERR_PTR(err) if fail
*/
struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
const char **supported_cable)
{
struct extcon_dev **ptr, *edev;
ptr = devres_alloc(devm_extcon_dev_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
edev = extcon_dev_allocate(supported_cable);
if (IS_ERR(edev)) {
devres_free(ptr);
return edev;
}
*ptr = edev;
devres_add(dev, ptr);
return edev;
}
EXPORT_SYMBOL_GPL(devm_extcon_dev_allocate);
void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev)
{
WARN_ON(devres_release(dev, devm_extcon_dev_release,
devm_extcon_dev_match, edev));
}
EXPORT_SYMBOL_GPL(devm_extcon_dev_free);
/** /**
* extcon_dev_register() - Register a new extcon device * extcon_dev_register() - Register a new extcon device
* @edev : the new extcon device (should be allocated before calling) * @edev : the new extcon device (should be allocated before calling)
...@@ -819,6 +913,63 @@ void extcon_dev_unregister(struct extcon_dev *edev) ...@@ -819,6 +913,63 @@ void extcon_dev_unregister(struct extcon_dev *edev)
} }
EXPORT_SYMBOL_GPL(extcon_dev_unregister); EXPORT_SYMBOL_GPL(extcon_dev_unregister);
static void devm_extcon_dev_unreg(struct device *dev, void *res)
{
extcon_dev_unregister(*(struct extcon_dev **)res);
}
/**
* devm_extcon_dev_register() - Resource-managed extcon_dev_register()
* @dev: device to allocate extcon device
* @edev: the new extcon device to register
*
* Managed extcon_dev_register() function. If extcon device is attached with
* this function, that extcon device is automatically unregistered on driver
* detach. Internally this function calls extcon_dev_register() function.
* To get more information, refer that function.
*
* If extcon device is registered with this function and the device needs to be
* unregistered separately, devm_extcon_dev_unregister() should be used.
*
* Returns 0 if success or negaive error number if failure.
*/
int devm_extcon_dev_register(struct device *dev, struct extcon_dev *edev)
{
struct extcon_dev **ptr;
int ret;
ptr = devres_alloc(devm_extcon_dev_unreg, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return -ENOMEM;
ret = extcon_dev_register(edev);
if (ret) {
devres_free(ptr);
return ret;
}
*ptr = edev;
devres_add(dev, ptr);
return 0;
}
EXPORT_SYMBOL_GPL(devm_extcon_dev_register);
/**
* devm_extcon_dev_unregister() - Resource-managed extcon_dev_unregister()
* @dev: device the extcon belongs to
* @edev: the extcon device to unregister
*
* Unregister extcon device that is registered with devm_extcon_dev_register()
* function.
*/
void devm_extcon_dev_unregister(struct device *dev, struct extcon_dev *edev)
{
WARN_ON(devres_release(dev, devm_extcon_dev_unreg,
devm_extcon_dev_match, edev));
}
EXPORT_SYMBOL_GPL(devm_extcon_dev_unregister);
#ifdef CONFIG_OF #ifdef CONFIG_OF
/* /*
* extcon_get_edev_by_phandle - Get the extcon device from devicetree * extcon_get_edev_by_phandle - Get the extcon device from devicetree
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
#include <linux/extcon/extcon-gpio.h> #include <linux/extcon/extcon-gpio.h>
struct gpio_extcon_data { struct gpio_extcon_data {
struct extcon_dev edev; struct extcon_dev *edev;
unsigned gpio; unsigned gpio;
bool gpio_active_low; bool gpio_active_low;
const char *state_on; const char *state_on;
...@@ -53,7 +53,7 @@ static void gpio_extcon_work(struct work_struct *work) ...@@ -53,7 +53,7 @@ static void gpio_extcon_work(struct work_struct *work)
state = gpio_get_value(data->gpio); state = gpio_get_value(data->gpio);
if (data->gpio_active_low) if (data->gpio_active_low)
state = !state; state = !state;
extcon_set_state(&data->edev, state); extcon_set_state(data->edev, state);
} }
static irqreturn_t gpio_irq_handler(int irq, void *dev_id) static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
...@@ -67,9 +67,10 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id) ...@@ -67,9 +67,10 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf) static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf)
{ {
struct gpio_extcon_data *extcon_data = struct device *dev = edev->dev.parent;
container_of(edev, struct gpio_extcon_data, edev); struct gpio_extcon_data *extcon_data = dev_get_drvdata(dev);
const char *state; const char *state;
if (extcon_get_state(edev)) if (extcon_get_state(edev))
state = extcon_data->state_on; state = extcon_data->state_on;
else else
...@@ -98,15 +99,21 @@ static int gpio_extcon_probe(struct platform_device *pdev) ...@@ -98,15 +99,21 @@ static int gpio_extcon_probe(struct platform_device *pdev)
if (!extcon_data) if (!extcon_data)
return -ENOMEM; return -ENOMEM;
extcon_data->edev.name = pdata->name; extcon_data->edev = devm_extcon_dev_allocate(&pdev->dev, NULL);
extcon_data->edev.dev.parent = &pdev->dev; if (IS_ERR(extcon_data->edev)) {
dev_err(&pdev->dev, "failed to allocate extcon device\n");
return -ENOMEM;
}
extcon_data->edev->name = pdata->name;
extcon_data->edev->dev.parent = &pdev->dev;
extcon_data->gpio = pdata->gpio; extcon_data->gpio = pdata->gpio;
extcon_data->gpio_active_low = pdata->gpio_active_low; extcon_data->gpio_active_low = pdata->gpio_active_low;
extcon_data->state_on = pdata->state_on; extcon_data->state_on = pdata->state_on;
extcon_data->state_off = pdata->state_off; extcon_data->state_off = pdata->state_off;
extcon_data->check_on_resume = pdata->check_on_resume; extcon_data->check_on_resume = pdata->check_on_resume;
if (pdata->state_on && pdata->state_off) if (pdata->state_on && pdata->state_off)
extcon_data->edev.print_state = extcon_gpio_print_state; extcon_data->edev->print_state = extcon_gpio_print_state;
ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN, ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN,
pdev->name); pdev->name);
...@@ -121,34 +128,27 @@ static int gpio_extcon_probe(struct platform_device *pdev) ...@@ -121,34 +128,27 @@ static int gpio_extcon_probe(struct platform_device *pdev)
msecs_to_jiffies(pdata->debounce); msecs_to_jiffies(pdata->debounce);
} }
ret = extcon_dev_register(&extcon_data->edev); ret = devm_extcon_dev_register(&pdev->dev, extcon_data->edev);
if (ret < 0) if (ret < 0)
return ret; return ret;
INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work); INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work);
extcon_data->irq = gpio_to_irq(extcon_data->gpio); extcon_data->irq = gpio_to_irq(extcon_data->gpio);
if (extcon_data->irq < 0) { if (extcon_data->irq < 0)
ret = extcon_data->irq; return extcon_data->irq;
goto err;
}
ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler, ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler,
pdata->irq_flags, pdev->name, pdata->irq_flags, pdev->name,
extcon_data); extcon_data);
if (ret < 0) if (ret < 0)
goto err; return ret;
platform_set_drvdata(pdev, extcon_data); platform_set_drvdata(pdev, extcon_data);
/* Perform initial detection */ /* Perform initial detection */
gpio_extcon_work(&extcon_data->work.work); gpio_extcon_work(&extcon_data->work.work);
return 0; return 0;
err:
extcon_dev_unregister(&extcon_data->edev);
return ret;
} }
static int gpio_extcon_remove(struct platform_device *pdev) static int gpio_extcon_remove(struct platform_device *pdev)
...@@ -157,7 +157,6 @@ static int gpio_extcon_remove(struct platform_device *pdev) ...@@ -157,7 +157,6 @@ static int gpio_extcon_remove(struct platform_device *pdev)
cancel_delayed_work_sync(&extcon_data->work); cancel_delayed_work_sync(&extcon_data->work);
free_irq(extcon_data->irq, extcon_data); free_irq(extcon_data->irq, extcon_data);
extcon_dev_unregister(&extcon_data->edev);
return 0; return 0;
} }
......
/* /*
* extcon-max14577.c - MAX14577 extcon driver to support MAX14577 MUIC * extcon-max14577.c - MAX14577/77836 extcon driver to support MUIC
* *
* Copyright (C) 2013 Samsung Electrnoics * Copyright (C) 2013,2014 Samsung Electrnoics
* Chanwoo Choi <cw00.choi@samsung.com> * Chanwoo Choi <cw00.choi@samsung.com>
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -24,7 +25,6 @@ ...@@ -24,7 +25,6 @@
#include <linux/mfd/max14577-private.h> #include <linux/mfd/max14577-private.h>
#include <linux/extcon.h> #include <linux/extcon.h>
#define DEV_NAME "max14577-muic"
#define DELAY_MS_DEFAULT 17000 /* unit: millisecond */ #define DELAY_MS_DEFAULT 17000 /* unit: millisecond */
enum max14577_muic_adc_debounce_time { enum max14577_muic_adc_debounce_time {
...@@ -40,6 +40,42 @@ enum max14577_muic_status { ...@@ -40,6 +40,42 @@ enum max14577_muic_status {
MAX14577_MUIC_STATUS_END, MAX14577_MUIC_STATUS_END,
}; };
/**
* struct max14577_muic_irq
* @irq: the index of irq list of MUIC device.
* @name: the name of irq.
* @virq: the virtual irq to use irq domain
*/
struct max14577_muic_irq {
unsigned int irq;
const char *name;
unsigned int virq;
};
static struct max14577_muic_irq max14577_muic_irqs[] = {
{ MAX14577_IRQ_INT1_ADC, "muic-ADC" },
{ MAX14577_IRQ_INT1_ADCLOW, "muic-ADCLOW" },
{ MAX14577_IRQ_INT1_ADCERR, "muic-ADCError" },
{ MAX14577_IRQ_INT2_CHGTYP, "muic-CHGTYP" },
{ MAX14577_IRQ_INT2_CHGDETRUN, "muic-CHGDETRUN" },
{ MAX14577_IRQ_INT2_DCDTMR, "muic-DCDTMR" },
{ MAX14577_IRQ_INT2_DBCHG, "muic-DBCHG" },
{ MAX14577_IRQ_INT2_VBVOLT, "muic-VBVOLT" },
};
static struct max14577_muic_irq max77836_muic_irqs[] = {
{ MAX14577_IRQ_INT1_ADC, "muic-ADC" },
{ MAX14577_IRQ_INT1_ADCLOW, "muic-ADCLOW" },
{ MAX14577_IRQ_INT1_ADCERR, "muic-ADCError" },
{ MAX77836_IRQ_INT1_ADC1K, "muic-ADC1K" },
{ MAX14577_IRQ_INT2_CHGTYP, "muic-CHGTYP" },
{ MAX14577_IRQ_INT2_CHGDETRUN, "muic-CHGDETRUN" },
{ MAX14577_IRQ_INT2_DCDTMR, "muic-DCDTMR" },
{ MAX14577_IRQ_INT2_DBCHG, "muic-DBCHG" },
{ MAX14577_IRQ_INT2_VBVOLT, "muic-VBVOLT" },
{ MAX77836_IRQ_INT2_VIDRM, "muic-VIDRM" },
};
struct max14577_muic_info { struct max14577_muic_info {
struct device *dev; struct device *dev;
struct max14577 *max14577; struct max14577 *max14577;
...@@ -48,6 +84,8 @@ struct max14577_muic_info { ...@@ -48,6 +84,8 @@ struct max14577_muic_info {
int prev_chg_type; int prev_chg_type;
u8 status[MAX14577_MUIC_STATUS_END]; u8 status[MAX14577_MUIC_STATUS_END];
struct max14577_muic_irq *muic_irqs;
unsigned int muic_irqs_num;
bool irq_adc; bool irq_adc;
bool irq_chg; bool irq_chg;
struct work_struct irq_work; struct work_struct irq_work;
...@@ -74,29 +112,6 @@ enum max14577_muic_cable_group { ...@@ -74,29 +112,6 @@ enum max14577_muic_cable_group {
MAX14577_CABLE_GROUP_CHG, MAX14577_CABLE_GROUP_CHG,
}; };
/**
* struct max14577_muic_irq
* @irq: the index of irq list of MUIC device.
* @name: the name of irq.
* @virq: the virtual irq to use irq domain
*/
struct max14577_muic_irq {
unsigned int irq;
const char *name;
unsigned int virq;
};
static struct max14577_muic_irq muic_irqs[] = {
{ MAX14577_IRQ_INT1_ADC, "muic-ADC" },
{ MAX14577_IRQ_INT1_ADCLOW, "muic-ADCLOW" },
{ MAX14577_IRQ_INT1_ADCERR, "muic-ADCError" },
{ MAX14577_IRQ_INT2_CHGTYP, "muic-CHGTYP" },
{ MAX14577_IRQ_INT2_CHGDETRUN, "muic-CHGDETRUN" },
{ MAX14577_IRQ_INT2_DCDTMR, "muic-DCDTMR" },
{ MAX14577_IRQ_INT2_DBCHG, "muic-DBCHG" },
{ MAX14577_IRQ_INT2_VBVOLT, "muic-VBVOLT" },
};
/* Define supported accessory type */ /* Define supported accessory type */
enum max14577_muic_acc_type { enum max14577_muic_acc_type {
MAX14577_MUIC_ADC_GROUND = 0x0, MAX14577_MUIC_ADC_GROUND = 0x0,
...@@ -528,21 +543,12 @@ static void max14577_muic_irq_work(struct work_struct *work) ...@@ -528,21 +543,12 @@ static void max14577_muic_irq_work(struct work_struct *work)
return; return;
} }
static irqreturn_t max14577_muic_irq_handler(int irq, void *data) /*
* Sets irq_adc or irq_chg in max14577_muic_info and returns 1.
* Returns 0 if irq_type does not match registered IRQ for this device type.
*/
static int max14577_parse_irq(struct max14577_muic_info *info, int irq_type)
{ {
struct max14577_muic_info *info = data;
int i, irq_type = -1;
/*
* We may be called multiple times for different nested IRQ-s.
* Including changes in INT1_ADC and INT2_CGHTYP at once.
* However we only need to know whether it was ADC, charger
* or both interrupts so decode IRQ and turn on proper flags.
*/
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
if (irq == muic_irqs[i].virq)
irq_type = muic_irqs[i].irq;
switch (irq_type) { switch (irq_type) {
case MAX14577_IRQ_INT1_ADC: case MAX14577_IRQ_INT1_ADC:
case MAX14577_IRQ_INT1_ADCLOW: case MAX14577_IRQ_INT1_ADCLOW:
...@@ -550,7 +556,7 @@ static irqreturn_t max14577_muic_irq_handler(int irq, void *data) ...@@ -550,7 +556,7 @@ static irqreturn_t max14577_muic_irq_handler(int irq, void *data)
/* Handle all of accessory except for /* Handle all of accessory except for
type of charger accessory */ type of charger accessory */
info->irq_adc = true; info->irq_adc = true;
break; return 1;
case MAX14577_IRQ_INT2_CHGTYP: case MAX14577_IRQ_INT2_CHGTYP:
case MAX14577_IRQ_INT2_CHGDETRUN: case MAX14577_IRQ_INT2_CHGDETRUN:
case MAX14577_IRQ_INT2_DCDTMR: case MAX14577_IRQ_INT2_DCDTMR:
...@@ -558,8 +564,62 @@ static irqreturn_t max14577_muic_irq_handler(int irq, void *data) ...@@ -558,8 +564,62 @@ static irqreturn_t max14577_muic_irq_handler(int irq, void *data)
case MAX14577_IRQ_INT2_VBVOLT: case MAX14577_IRQ_INT2_VBVOLT:
/* Handle charger accessory */ /* Handle charger accessory */
info->irq_chg = true; info->irq_chg = true;
return 1;
default:
return 0;
}
}
/*
* Sets irq_adc or irq_chg in max14577_muic_info and returns 1.
* Returns 0 if irq_type does not match registered IRQ for this device type.
*/
static int max77836_parse_irq(struct max14577_muic_info *info, int irq_type)
{
/* First check common max14577 interrupts */
if (max14577_parse_irq(info, irq_type))
return 1;
switch (irq_type) {
case MAX77836_IRQ_INT1_ADC1K:
info->irq_adc = true;
return 1;
case MAX77836_IRQ_INT2_VIDRM:
/* Handle charger accessory */
info->irq_chg = true;
return 1;
default:
return 0;
}
}
static irqreturn_t max14577_muic_irq_handler(int irq, void *data)
{
struct max14577_muic_info *info = data;
int i, irq_type = -1;
bool irq_parsed;
/*
* We may be called multiple times for different nested IRQ-s.
* Including changes in INT1_ADC and INT2_CGHTYP at once.
* However we only need to know whether it was ADC, charger
* or both interrupts so decode IRQ and turn on proper flags.
*/
for (i = 0; i < info->muic_irqs_num; i++)
if (irq == info->muic_irqs[i].virq)
irq_type = info->muic_irqs[i].irq;
switch (info->max14577->dev_type) {
case MAXIM_DEVICE_TYPE_MAX77836:
irq_parsed = max77836_parse_irq(info, irq_type);
break; break;
case MAXIM_DEVICE_TYPE_MAX14577:
default: default:
irq_parsed = max14577_parse_irq(info, irq_type);
break;
}
if (!irq_parsed) {
dev_err(info->dev, "muic interrupt: irq %d occurred, skipped\n", dev_err(info->dev, "muic interrupt: irq %d occurred, skipped\n",
irq_type); irq_type);
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -644,13 +704,24 @@ static int max14577_muic_probe(struct platform_device *pdev) ...@@ -644,13 +704,24 @@ static int max14577_muic_probe(struct platform_device *pdev)
INIT_WORK(&info->irq_work, max14577_muic_irq_work); INIT_WORK(&info->irq_work, max14577_muic_irq_work);
switch (max14577->dev_type) {
case MAXIM_DEVICE_TYPE_MAX77836:
info->muic_irqs = max77836_muic_irqs;
info->muic_irqs_num = ARRAY_SIZE(max77836_muic_irqs);
break;
case MAXIM_DEVICE_TYPE_MAX14577:
default:
info->muic_irqs = max14577_muic_irqs;
info->muic_irqs_num = ARRAY_SIZE(max14577_muic_irqs);
}
/* Support irq domain for max14577 MUIC device */ /* Support irq domain for max14577 MUIC device */
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) { for (i = 0; i < info->muic_irqs_num; i++) {
struct max14577_muic_irq *muic_irq = &muic_irqs[i]; struct max14577_muic_irq *muic_irq = &info->muic_irqs[i];
unsigned int virq = 0; unsigned int virq = 0;
virq = regmap_irq_get_virq(max14577->irq_data, muic_irq->irq); virq = regmap_irq_get_virq(max14577->irq_data, muic_irq->irq);
if (!virq) if (virq <= 0)
return -EINVAL; return -EINVAL;
muic_irq->virq = virq; muic_irq->virq = virq;
...@@ -668,14 +739,16 @@ static int max14577_muic_probe(struct platform_device *pdev) ...@@ -668,14 +739,16 @@ static int max14577_muic_probe(struct platform_device *pdev)
} }
/* Initialize extcon device */ /* Initialize extcon device */
info->edev = devm_kzalloc(&pdev->dev, sizeof(*info->edev), GFP_KERNEL); info->edev = devm_extcon_dev_allocate(&pdev->dev,
if (!info->edev) { max14577_extcon_cable);
if (IS_ERR(info->edev)) {
dev_err(&pdev->dev, "failed to allocate memory for extcon\n"); dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
return -ENOMEM; return -ENOMEM;
} }
info->edev->name = DEV_NAME;
info->edev->supported_cable = max14577_extcon_cable; info->edev->name = dev_name(&pdev->dev);
ret = extcon_dev_register(info->edev);
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to register extcon device\n"); dev_err(&pdev->dev, "failed to register extcon device\n");
return ret; return ret;
...@@ -694,7 +767,7 @@ static int max14577_muic_probe(struct platform_device *pdev) ...@@ -694,7 +767,7 @@ static int max14577_muic_probe(struct platform_device *pdev)
MAX14577_REG_DEVICEID, &id); MAX14577_REG_DEVICEID, &id);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "failed to read revision number\n"); dev_err(&pdev->dev, "failed to read revision number\n");
goto err_extcon; return ret;
} }
dev_info(info->dev, "device ID : 0x%x\n", id); dev_info(info->dev, "device ID : 0x%x\n", id);
...@@ -710,19 +783,10 @@ static int max14577_muic_probe(struct platform_device *pdev) ...@@ -710,19 +783,10 @@ static int max14577_muic_probe(struct platform_device *pdev)
* driver should notify cable state to upper layer. * driver should notify cable state to upper layer.
*/ */
INIT_DELAYED_WORK(&info->wq_detcable, max14577_muic_detect_cable_wq); INIT_DELAYED_WORK(&info->wq_detcable, max14577_muic_detect_cable_wq);
ret = queue_delayed_work(system_power_efficient_wq, &info->wq_detcable, queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
delay_jiffies); delay_jiffies);
if (ret < 0) {
dev_err(&pdev->dev,
"failed to schedule delayed work for cable detect\n");
goto err_extcon;
}
return ret; return ret;
err_extcon:
extcon_dev_unregister(info->edev);
return ret;
} }
static int max14577_muic_remove(struct platform_device *pdev) static int max14577_muic_remove(struct platform_device *pdev)
...@@ -730,23 +794,30 @@ static int max14577_muic_remove(struct platform_device *pdev) ...@@ -730,23 +794,30 @@ static int max14577_muic_remove(struct platform_device *pdev)
struct max14577_muic_info *info = platform_get_drvdata(pdev); struct max14577_muic_info *info = platform_get_drvdata(pdev);
cancel_work_sync(&info->irq_work); cancel_work_sync(&info->irq_work);
extcon_dev_unregister(info->edev);
return 0; return 0;
} }
static const struct platform_device_id max14577_muic_id[] = {
{ "max14577-muic", MAXIM_DEVICE_TYPE_MAX14577, },
{ "max77836-muic", MAXIM_DEVICE_TYPE_MAX77836, },
{ }
};
MODULE_DEVICE_TABLE(platform, max14577_muic_id);
static struct platform_driver max14577_muic_driver = { static struct platform_driver max14577_muic_driver = {
.driver = { .driver = {
.name = DEV_NAME, .name = "max14577-muic",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
.probe = max14577_muic_probe, .probe = max14577_muic_probe,
.remove = max14577_muic_remove, .remove = max14577_muic_remove,
.id_table = max14577_muic_id,
}; };
module_platform_driver(max14577_muic_driver); module_platform_driver(max14577_muic_driver);
MODULE_DESCRIPTION("MAXIM 14577 Extcon driver"); MODULE_DESCRIPTION("Maxim 14577/77836 Extcon driver");
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <k.kozlowski@samsung.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:extcon-max14577"); MODULE_ALIAS("platform:extcon-max14577");
...@@ -1175,25 +1175,24 @@ static int max77693_muic_probe(struct platform_device *pdev) ...@@ -1175,25 +1175,24 @@ static int max77693_muic_probe(struct platform_device *pdev)
} }
/* Initialize extcon device */ /* Initialize extcon device */
info->edev = devm_kzalloc(&pdev->dev, sizeof(struct extcon_dev), info->edev = devm_extcon_dev_allocate(&pdev->dev,
GFP_KERNEL); max77693_extcon_cable);
if (!info->edev) { if (IS_ERR(info->edev)) {
dev_err(&pdev->dev, "failed to allocate memory for extcon\n"); dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
ret = -ENOMEM; ret = -ENOMEM;
goto err_irq; goto err_irq;
} }
info->edev->name = DEV_NAME; info->edev->name = DEV_NAME;
info->edev->dev.parent = &pdev->dev; info->edev->dev.parent = &pdev->dev;
info->edev->supported_cable = max77693_extcon_cable;
ret = extcon_dev_register(info->edev); ret = devm_extcon_dev_register(&pdev->dev, info->edev);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to register extcon device\n"); dev_err(&pdev->dev, "failed to register extcon device\n");
goto err_irq; goto err_irq;
} }
/* Initialize MUIC register by using platform data or default data */ /* Initialize MUIC register by using platform data or default data */
if (pdata->muic_data) { if (pdata && pdata->muic_data) {
init_data = pdata->muic_data->init_data; init_data = pdata->muic_data->init_data;
num_init_data = pdata->muic_data->num_init_data; num_init_data = pdata->muic_data->num_init_data;
} else { } else {
...@@ -1226,7 +1225,7 @@ static int max77693_muic_probe(struct platform_device *pdev) ...@@ -1226,7 +1225,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
= init_data[i].data; = init_data[i].data;
} }
if (pdata->muic_data) { if (pdata && pdata->muic_data) {
struct max77693_muic_platform_data *muic_pdata struct max77693_muic_platform_data *muic_pdata
= pdata->muic_data; = pdata->muic_data;
...@@ -1267,7 +1266,7 @@ static int max77693_muic_probe(struct platform_device *pdev) ...@@ -1267,7 +1266,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
MAX77693_MUIC_REG_ID, &id); MAX77693_MUIC_REG_ID, &id);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "failed to read revision number\n"); dev_err(&pdev->dev, "failed to read revision number\n");
goto err_extcon; goto err_irq;
} }
dev_info(info->dev, "device ID : 0x%x\n", id); dev_info(info->dev, "device ID : 0x%x\n", id);
...@@ -1283,12 +1282,11 @@ static int max77693_muic_probe(struct platform_device *pdev) ...@@ -1283,12 +1282,11 @@ static int max77693_muic_probe(struct platform_device *pdev)
* driver should notify cable state to upper layer. * driver should notify cable state to upper layer.
*/ */
INIT_DELAYED_WORK(&info->wq_detcable, max77693_muic_detect_cable_wq); INIT_DELAYED_WORK(&info->wq_detcable, max77693_muic_detect_cable_wq);
schedule_delayed_work(&info->wq_detcable, delay_jiffies); queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
delay_jiffies);
return ret; return ret;
err_extcon:
extcon_dev_unregister(info->edev);
err_irq: err_irq:
while (--i >= 0) while (--i >= 0)
free_irq(muic_irqs[i].virq, info); free_irq(muic_irqs[i].virq, info);
...@@ -1304,7 +1302,6 @@ static int max77693_muic_remove(struct platform_device *pdev) ...@@ -1304,7 +1302,6 @@ static int max77693_muic_remove(struct platform_device *pdev)
free_irq(muic_irqs[i].virq, info); free_irq(muic_irqs[i].virq, info);
cancel_work_sync(&info->irq_work); cancel_work_sync(&info->irq_work);
input_unregister_device(info->dock); input_unregister_device(info->dock);
extcon_dev_unregister(info->edev);
return 0; return 0;
} }
......
...@@ -699,23 +699,22 @@ static int max8997_muic_probe(struct platform_device *pdev) ...@@ -699,23 +699,22 @@ static int max8997_muic_probe(struct platform_device *pdev)
} }
/* External connector */ /* External connector */
info->edev = devm_kzalloc(&pdev->dev, sizeof(struct extcon_dev), info->edev = devm_extcon_dev_allocate(&pdev->dev, max8997_extcon_cable);
GFP_KERNEL); if (IS_ERR(info->edev)) {
if (!info->edev) {
dev_err(&pdev->dev, "failed to allocate memory for extcon\n"); dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
ret = -ENOMEM; ret = -ENOMEM;
goto err_irq; goto err_irq;
} }
info->edev->name = DEV_NAME; info->edev->name = DEV_NAME;
info->edev->dev.parent = &pdev->dev; info->edev->dev.parent = &pdev->dev;
info->edev->supported_cable = max8997_extcon_cable;
ret = extcon_dev_register(info->edev); ret = devm_extcon_dev_register(&pdev->dev, info->edev);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to register extcon device\n"); dev_err(&pdev->dev, "failed to register extcon device\n");
goto err_irq; goto err_irq;
} }
if (pdata->muic_pdata) { if (pdata && pdata->muic_pdata) {
struct max8997_muic_platform_data *muic_pdata struct max8997_muic_platform_data *muic_pdata
= pdata->muic_pdata; = pdata->muic_pdata;
...@@ -770,7 +769,8 @@ static int max8997_muic_probe(struct platform_device *pdev) ...@@ -770,7 +769,8 @@ static int max8997_muic_probe(struct platform_device *pdev)
* driver should notify cable state to upper layer. * driver should notify cable state to upper layer.
*/ */
INIT_DELAYED_WORK(&info->wq_detcable, max8997_muic_detect_cable_wq); INIT_DELAYED_WORK(&info->wq_detcable, max8997_muic_detect_cable_wq);
schedule_delayed_work(&info->wq_detcable, delay_jiffies); queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
delay_jiffies);
return 0; return 0;
...@@ -789,8 +789,6 @@ static int max8997_muic_remove(struct platform_device *pdev) ...@@ -789,8 +789,6 @@ static int max8997_muic_remove(struct platform_device *pdev)
free_irq(muic_irqs[i].virq, info); free_irq(muic_irqs[i].virq, info);
cancel_work_sync(&info->irq_work); cancel_work_sync(&info->irq_work);
extcon_dev_unregister(info->edev);
return 0; return 0;
} }
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/mfd/palmas.h> #include <linux/mfd/palmas.h>
#include <linux/of.h> #include <linux/of.h>
...@@ -56,7 +57,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) ...@@ -56,7 +57,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) { if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) {
if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) { if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) {
palmas_usb->linkstat = PALMAS_USB_STATE_VBUS; palmas_usb->linkstat = PALMAS_USB_STATE_VBUS;
extcon_set_cable_state(&palmas_usb->edev, "USB", true); extcon_set_cable_state(palmas_usb->edev, "USB", true);
dev_info(palmas_usb->dev, "USB cable is attached\n"); dev_info(palmas_usb->dev, "USB cable is attached\n");
} else { } else {
dev_dbg(palmas_usb->dev, dev_dbg(palmas_usb->dev,
...@@ -65,7 +66,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) ...@@ -65,7 +66,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
} else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) { } else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) {
if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) { if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) {
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
extcon_set_cable_state(&palmas_usb->edev, "USB", false); extcon_set_cable_state(palmas_usb->edev, "USB", false);
dev_info(palmas_usb->dev, "USB cable is detached\n"); dev_info(palmas_usb->dev, "USB cable is detached\n");
} else { } else {
dev_dbg(palmas_usb->dev, dev_dbg(palmas_usb->dev,
...@@ -92,7 +93,7 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) ...@@ -92,7 +93,7 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
PALMAS_USB_ID_INT_LATCH_CLR, PALMAS_USB_ID_INT_LATCH_CLR,
PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND); PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND);
palmas_usb->linkstat = PALMAS_USB_STATE_ID; palmas_usb->linkstat = PALMAS_USB_STATE_ID;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true); extcon_set_cable_state(palmas_usb->edev, "USB-HOST", true);
dev_info(palmas_usb->dev, "USB-HOST cable is attached\n"); dev_info(palmas_usb->dev, "USB-HOST cable is attached\n");
} else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) && } else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) &&
(id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) { (id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) {
...@@ -100,17 +101,17 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) ...@@ -100,17 +101,17 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
PALMAS_USB_ID_INT_LATCH_CLR, PALMAS_USB_ID_INT_LATCH_CLR,
PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT); PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT);
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false); extcon_set_cable_state(palmas_usb->edev, "USB-HOST", false);
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) && } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) &&
(!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) { (!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) {
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false); extcon_set_cable_state(palmas_usb->edev, "USB-HOST", false);
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) && } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) &&
(id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) { (id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) {
palmas_usb->linkstat = PALMAS_USB_STATE_ID; palmas_usb->linkstat = PALMAS_USB_STATE_ID;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true); extcon_set_cable_state(palmas_usb->edev, "USB-HOST", true);
dev_info(palmas_usb->dev, " USB-HOST cable is attached\n"); dev_info(palmas_usb->dev, " USB-HOST cable is attached\n");
} }
...@@ -186,13 +187,20 @@ static int palmas_usb_probe(struct platform_device *pdev) ...@@ -186,13 +187,20 @@ static int palmas_usb_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, palmas_usb); platform_set_drvdata(pdev, palmas_usb);
palmas_usb->edev.supported_cable = palmas_extcon_cable; palmas_usb->edev = devm_extcon_dev_allocate(&pdev->dev,
palmas_usb->edev.dev.parent = palmas_usb->dev; palmas_extcon_cable);
palmas_usb->edev.mutually_exclusive = mutually_exclusive; if (IS_ERR(palmas_usb->edev)) {
dev_err(&pdev->dev, "failed to allocate extcon device\n");
return -ENOMEM;
}
palmas_usb->edev->name = kstrdup(node->name, GFP_KERNEL);
palmas_usb->edev->dev.parent = palmas_usb->dev;
palmas_usb->edev->mutually_exclusive = mutually_exclusive;
status = extcon_dev_register(&palmas_usb->edev); status = devm_extcon_dev_register(&pdev->dev, palmas_usb->edev);
if (status) { if (status) {
dev_err(&pdev->dev, "failed to register extcon device\n"); dev_err(&pdev->dev, "failed to register extcon device\n");
kfree(palmas_usb->edev->name);
return status; return status;
} }
...@@ -206,7 +214,8 @@ static int palmas_usb_probe(struct platform_device *pdev) ...@@ -206,7 +214,8 @@ static int palmas_usb_probe(struct platform_device *pdev)
if (status < 0) { if (status < 0) {
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
palmas_usb->id_irq, status); palmas_usb->id_irq, status);
goto fail_extcon; kfree(palmas_usb->edev->name);
return status;
} }
} }
...@@ -220,25 +229,21 @@ static int palmas_usb_probe(struct platform_device *pdev) ...@@ -220,25 +229,21 @@ static int palmas_usb_probe(struct platform_device *pdev)
if (status < 0) { if (status < 0) {
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
palmas_usb->vbus_irq, status); palmas_usb->vbus_irq, status);
goto fail_extcon; kfree(palmas_usb->edev->name);
return status;
} }
} }
palmas_enable_irq(palmas_usb); palmas_enable_irq(palmas_usb);
device_set_wakeup_capable(&pdev->dev, true); device_set_wakeup_capable(&pdev->dev, true);
return 0; return 0;
fail_extcon:
extcon_dev_unregister(&palmas_usb->edev);
return status;
} }
static int palmas_usb_remove(struct platform_device *pdev) static int palmas_usb_remove(struct platform_device *pdev)
{ {
struct palmas_usb *palmas_usb = platform_get_drvdata(pdev); struct palmas_usb *palmas_usb = platform_get_drvdata(pdev);
extcon_dev_unregister(&palmas_usb->edev); kfree(palmas_usb->edev->name);
return 0; return 0;
} }
......
...@@ -331,15 +331,15 @@ config MFD_88PM860X ...@@ -331,15 +331,15 @@ config MFD_88PM860X
battery-charger under the corresponding menus. battery-charger under the corresponding menus.
config MFD_MAX14577 config MFD_MAX14577
bool "Maxim Semiconductor MAX14577 MUIC + Charger Support" bool "Maxim Semiconductor MAX14577/77836 MUIC + Charger Support"
depends on I2C=y depends on I2C=y
select MFD_CORE select MFD_CORE
select REGMAP_I2C select REGMAP_I2C
select REGMAP_IRQ select REGMAP_IRQ
select IRQ_DOMAIN select IRQ_DOMAIN
help help
Say yes here to add support for Maxim Semiconductor MAX14577. Say yes here to add support for Maxim Semiconductor MAX14577 and
This is a Micro-USB IC with Charger controls on chip. MAX77836 Micro-USB ICs with battery charger.
This driver provides common support for accessing the device; This driver provides common support for accessing the device;
additional drivers must be enabled in order to use the functionality additional drivers must be enabled in order to use the functionality
of the device. of the device.
......
/* /*
* max14577.c - mfd core driver for the Maxim 14577 * max14577.c - mfd core driver for the Maxim 14577/77836
* *
* Copyright (C) 2013 Samsung Electrnoics * Copyright (C) 2014 Samsung Electrnoics
* Chanwoo Choi <cw00.choi@samsung.com> * Chanwoo Choi <cw00.choi@samsung.com>
* Krzysztof Kozlowski <k.kozlowski@samsung.com> * Krzysztof Kozlowski <k.kozlowski@samsung.com>
* *
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/of_device.h>
#include <linux/mfd/core.h> #include <linux/mfd/core.h>
#include <linux/mfd/max14577.h> #include <linux/mfd/max14577.h>
#include <linux/mfd/max14577-private.h> #include <linux/mfd/max14577-private.h>
...@@ -37,7 +38,38 @@ static struct mfd_cell max14577_devs[] = { ...@@ -37,7 +38,38 @@ static struct mfd_cell max14577_devs[] = {
{ .name = "max14577-charger", }, { .name = "max14577-charger", },
}; };
static bool max14577_volatile_reg(struct device *dev, unsigned int reg) static struct mfd_cell max77836_devs[] = {
{
.name = "max77836-muic",
.of_compatible = "maxim,max77836-muic",
},
{
.name = "max77836-regulator",
.of_compatible = "maxim,max77836-regulator",
},
{
.name = "max77836-charger",
.of_compatible = "maxim,max77836-charger",
},
{
.name = "max77836-battery",
.of_compatible = "maxim,max77836-battery",
},
};
static struct of_device_id max14577_dt_match[] = {
{
.compatible = "maxim,max14577",
.data = (void *)MAXIM_DEVICE_TYPE_MAX14577,
},
{
.compatible = "maxim,max77836",
.data = (void *)MAXIM_DEVICE_TYPE_MAX77836,
},
{},
};
static bool max14577_muic_volatile_reg(struct device *dev, unsigned int reg)
{ {
switch (reg) { switch (reg) {
case MAX14577_REG_INT1 ... MAX14577_REG_STATUS3: case MAX14577_REG_INT1 ... MAX14577_REG_STATUS3:
...@@ -48,49 +80,221 @@ static bool max14577_volatile_reg(struct device *dev, unsigned int reg) ...@@ -48,49 +80,221 @@ static bool max14577_volatile_reg(struct device *dev, unsigned int reg)
return false; return false;
} }
static const struct regmap_config max14577_regmap_config = { static bool max77836_muic_volatile_reg(struct device *dev, unsigned int reg)
{
/* Any max14577 volatile registers are also max77836 volatile. */
if (max14577_muic_volatile_reg(dev, reg))
return true;
switch (reg) {
case MAX77836_FG_REG_VCELL_MSB ... MAX77836_FG_REG_SOC_LSB:
case MAX77836_FG_REG_CRATE_MSB ... MAX77836_FG_REG_CRATE_LSB:
case MAX77836_FG_REG_STATUS_H ... MAX77836_FG_REG_STATUS_L:
case MAX77836_PMIC_REG_INTSRC:
case MAX77836_PMIC_REG_TOPSYS_INT:
case MAX77836_PMIC_REG_TOPSYS_STAT:
return true;
default:
break;
}
return false;
}
static const struct regmap_config max14577_muic_regmap_config = {
.reg_bits = 8, .reg_bits = 8,
.val_bits = 8, .val_bits = 8,
.volatile_reg = max14577_volatile_reg, .volatile_reg = max14577_muic_volatile_reg,
.max_register = MAX14577_REG_END, .max_register = MAX14577_REG_END,
}; };
static const struct regmap_config max77836_pmic_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.volatile_reg = max77836_muic_volatile_reg,
.max_register = MAX77836_PMIC_REG_END,
};
static const struct regmap_irq max14577_irqs[] = { static const struct regmap_irq max14577_irqs[] = {
/* INT1 interrupts */ /* INT1 interrupts */
{ .reg_offset = 0, .mask = INT1_ADC_MASK, }, { .reg_offset = 0, .mask = MAX14577_INT1_ADC_MASK, },
{ .reg_offset = 0, .mask = INT1_ADCLOW_MASK, }, { .reg_offset = 0, .mask = MAX14577_INT1_ADCLOW_MASK, },
{ .reg_offset = 0, .mask = INT1_ADCERR_MASK, }, { .reg_offset = 0, .mask = MAX14577_INT1_ADCERR_MASK, },
/* INT2 interrupts */ /* INT2 interrupts */
{ .reg_offset = 1, .mask = INT2_CHGTYP_MASK, }, { .reg_offset = 1, .mask = MAX14577_INT2_CHGTYP_MASK, },
{ .reg_offset = 1, .mask = INT2_CHGDETRUN_MASK, }, { .reg_offset = 1, .mask = MAX14577_INT2_CHGDETRUN_MASK, },
{ .reg_offset = 1, .mask = INT2_DCDTMR_MASK, }, { .reg_offset = 1, .mask = MAX14577_INT2_DCDTMR_MASK, },
{ .reg_offset = 1, .mask = INT2_DBCHG_MASK, }, { .reg_offset = 1, .mask = MAX14577_INT2_DBCHG_MASK, },
{ .reg_offset = 1, .mask = INT2_VBVOLT_MASK, }, { .reg_offset = 1, .mask = MAX14577_INT2_VBVOLT_MASK, },
/* INT3 interrupts */ /* INT3 interrupts */
{ .reg_offset = 2, .mask = INT3_EOC_MASK, }, { .reg_offset = 2, .mask = MAX14577_INT3_EOC_MASK, },
{ .reg_offset = 2, .mask = INT3_CGMBC_MASK, }, { .reg_offset = 2, .mask = MAX14577_INT3_CGMBC_MASK, },
{ .reg_offset = 2, .mask = INT3_OVP_MASK, }, { .reg_offset = 2, .mask = MAX14577_INT3_OVP_MASK, },
{ .reg_offset = 2, .mask = INT3_MBCCHGERR_MASK, }, { .reg_offset = 2, .mask = MAX14577_INT3_MBCCHGERR_MASK, },
}; };
static const struct regmap_irq_chip max14577_irq_chip = { static const struct regmap_irq_chip max14577_irq_chip = {
.name = "max14577", .name = "max14577",
.status_base = MAX14577_REG_INT1, .status_base = MAX14577_REG_INT1,
.mask_base = MAX14577_REG_INTMASK1, .mask_base = MAX14577_REG_INTMASK1,
.mask_invert = 1, .mask_invert = true,
.num_regs = 3, .num_regs = 3,
.irqs = max14577_irqs, .irqs = max14577_irqs,
.num_irqs = ARRAY_SIZE(max14577_irqs), .num_irqs = ARRAY_SIZE(max14577_irqs),
}; };
static const struct regmap_irq max77836_muic_irqs[] = {
/* INT1 interrupts */
{ .reg_offset = 0, .mask = MAX14577_INT1_ADC_MASK, },
{ .reg_offset = 0, .mask = MAX14577_INT1_ADCLOW_MASK, },
{ .reg_offset = 0, .mask = MAX14577_INT1_ADCERR_MASK, },
{ .reg_offset = 0, .mask = MAX77836_INT1_ADC1K_MASK, },
/* INT2 interrupts */
{ .reg_offset = 1, .mask = MAX14577_INT2_CHGTYP_MASK, },
{ .reg_offset = 1, .mask = MAX14577_INT2_CHGDETRUN_MASK, },
{ .reg_offset = 1, .mask = MAX14577_INT2_DCDTMR_MASK, },
{ .reg_offset = 1, .mask = MAX14577_INT2_DBCHG_MASK, },
{ .reg_offset = 1, .mask = MAX14577_INT2_VBVOLT_MASK, },
{ .reg_offset = 1, .mask = MAX77836_INT2_VIDRM_MASK, },
/* INT3 interrupts */
{ .reg_offset = 2, .mask = MAX14577_INT3_EOC_MASK, },
{ .reg_offset = 2, .mask = MAX14577_INT3_CGMBC_MASK, },
{ .reg_offset = 2, .mask = MAX14577_INT3_OVP_MASK, },
{ .reg_offset = 2, .mask = MAX14577_INT3_MBCCHGERR_MASK, },
};
static const struct regmap_irq_chip max77836_muic_irq_chip = {
.name = "max77836-muic",
.status_base = MAX14577_REG_INT1,
.mask_base = MAX14577_REG_INTMASK1,
.mask_invert = true,
.num_regs = 3,
.irqs = max77836_muic_irqs,
.num_irqs = ARRAY_SIZE(max77836_muic_irqs),
};
static const struct regmap_irq max77836_pmic_irqs[] = {
{ .reg_offset = 0, .mask = MAX77836_TOPSYS_INT_T120C_MASK, },
{ .reg_offset = 0, .mask = MAX77836_TOPSYS_INT_T140C_MASK, },
};
static const struct regmap_irq_chip max77836_pmic_irq_chip = {
.name = "max77836-pmic",
.status_base = MAX77836_PMIC_REG_TOPSYS_INT,
.mask_base = MAX77836_PMIC_REG_TOPSYS_INT_MASK,
.mask_invert = false,
.num_regs = 1,
.irqs = max77836_pmic_irqs,
.num_irqs = ARRAY_SIZE(max77836_pmic_irqs),
};
static void max14577_print_dev_type(struct max14577 *max14577)
{
u8 reg_data, vendor_id, device_id;
int ret;
ret = max14577_read_reg(max14577->regmap, MAX14577_REG_DEVICEID,
&reg_data);
if (ret) {
dev_err(max14577->dev,
"Failed to read DEVICEID register: %d\n", ret);
return;
}
vendor_id = ((reg_data & DEVID_VENDORID_MASK) >>
DEVID_VENDORID_SHIFT);
device_id = ((reg_data & DEVID_DEVICEID_MASK) >>
DEVID_DEVICEID_SHIFT);
dev_info(max14577->dev, "Device type: %u (ID: 0x%x, vendor: 0x%x)\n",
max14577->dev_type, device_id, vendor_id);
}
/*
* Max77836 specific initialization code for driver probe.
* Adds new I2C dummy device, regmap and regmap IRQ chip.
* Unmasks Interrupt Source register.
*
* On success returns 0.
* On failure returns errno and reverts any changes done so far (e.g. remove
* I2C dummy device), except masking the INT SRC register.
*/
static int max77836_init(struct max14577 *max14577)
{
int ret;
u8 intsrc_mask;
max14577->i2c_pmic = i2c_new_dummy(max14577->i2c->adapter,
I2C_ADDR_PMIC);
if (!max14577->i2c_pmic) {
dev_err(max14577->dev, "Failed to register PMIC I2C device\n");
return -ENODEV;
}
i2c_set_clientdata(max14577->i2c_pmic, max14577);
max14577->regmap_pmic = devm_regmap_init_i2c(max14577->i2c_pmic,
&max77836_pmic_regmap_config);
if (IS_ERR(max14577->regmap_pmic)) {
ret = PTR_ERR(max14577->regmap_pmic);
dev_err(max14577->dev, "Failed to allocate PMIC register map: %d\n",
ret);
goto err;
}
/* Un-mask MAX77836 Interrupt Source register */
ret = max14577_read_reg(max14577->regmap_pmic,
MAX77836_PMIC_REG_INTSRC_MASK, &intsrc_mask);
if (ret < 0) {
dev_err(max14577->dev, "Failed to read PMIC register\n");
goto err;
}
intsrc_mask &= ~(MAX77836_INTSRC_MASK_TOP_INT_MASK);
intsrc_mask &= ~(MAX77836_INTSRC_MASK_MUIC_CHG_INT_MASK);
ret = max14577_write_reg(max14577->regmap_pmic,
MAX77836_PMIC_REG_INTSRC_MASK, intsrc_mask);
if (ret < 0) {
dev_err(max14577->dev, "Failed to write PMIC register\n");
goto err;
}
ret = regmap_add_irq_chip(max14577->regmap_pmic, max14577->irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED,
0, &max77836_pmic_irq_chip,
&max14577->irq_data_pmic);
if (ret != 0) {
dev_err(max14577->dev, "Failed to request PMIC IRQ %d: %d\n",
max14577->irq, ret);
goto err;
}
return 0;
err:
i2c_unregister_device(max14577->i2c_pmic);
return ret;
}
/*
* Max77836 specific de-initialization code for driver remove.
*/
static void max77836_remove(struct max14577 *max14577)
{
regmap_del_irq_chip(max14577->irq, max14577->irq_data_pmic);
i2c_unregister_device(max14577->i2c_pmic);
}
static int max14577_i2c_probe(struct i2c_client *i2c, static int max14577_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct max14577 *max14577; struct max14577 *max14577;
struct max14577_platform_data *pdata = dev_get_platdata(&i2c->dev); struct max14577_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct device_node *np = i2c->dev.of_node; struct device_node *np = i2c->dev.of_node;
u8 reg_data;
int ret = 0; int ret = 0;
const struct regmap_irq_chip *irq_chip;
struct mfd_cell *mfd_devs;
unsigned int mfd_devs_size;
int irq_flags;
if (np) { if (np) {
pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL); pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL);
...@@ -113,7 +317,8 @@ static int max14577_i2c_probe(struct i2c_client *i2c, ...@@ -113,7 +317,8 @@ static int max14577_i2c_probe(struct i2c_client *i2c,
max14577->i2c = i2c; max14577->i2c = i2c;
max14577->irq = i2c->irq; max14577->irq = i2c->irq;
max14577->regmap = devm_regmap_init_i2c(i2c, &max14577_regmap_config); max14577->regmap = devm_regmap_init_i2c(i2c,
&max14577_muic_regmap_config);
if (IS_ERR(max14577->regmap)) { if (IS_ERR(max14577->regmap)) {
ret = PTR_ERR(max14577->regmap); ret = PTR_ERR(max14577->regmap);
dev_err(max14577->dev, "Failed to allocate register map: %d\n", dev_err(max14577->dev, "Failed to allocate register map: %d\n",
...@@ -121,23 +326,36 @@ static int max14577_i2c_probe(struct i2c_client *i2c, ...@@ -121,23 +326,36 @@ static int max14577_i2c_probe(struct i2c_client *i2c,
return ret; return ret;
} }
ret = max14577_read_reg(max14577->regmap, MAX14577_REG_DEVICEID, if (np) {
&reg_data); const struct of_device_id *of_id;
if (ret) {
dev_err(max14577->dev, "Device not found on this channel: %d\n", of_id = of_match_device(max14577_dt_match, &i2c->dev);
ret); if (of_id)
return ret; max14577->dev_type = (unsigned int)of_id->data;
} else {
max14577->dev_type = id->driver_data;
}
max14577_print_dev_type(max14577);
switch (max14577->dev_type) {
case MAXIM_DEVICE_TYPE_MAX77836:
irq_chip = &max77836_muic_irq_chip;
mfd_devs = max77836_devs;
mfd_devs_size = ARRAY_SIZE(max77836_devs);
irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED;
break;
case MAXIM_DEVICE_TYPE_MAX14577:
default:
irq_chip = &max14577_irq_chip;
mfd_devs = max14577_devs;
mfd_devs_size = ARRAY_SIZE(max14577_devs);
irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
break;
} }
max14577->vendor_id = ((reg_data & DEVID_VENDORID_MASK) >>
DEVID_VENDORID_SHIFT);
max14577->device_id = ((reg_data & DEVID_DEVICEID_MASK) >>
DEVID_DEVICEID_SHIFT);
dev_info(max14577->dev, "Device ID: 0x%x, vendor: 0x%x\n",
max14577->device_id, max14577->vendor_id);
ret = regmap_add_irq_chip(max14577->regmap, max14577->irq, ret = regmap_add_irq_chip(max14577->regmap, max14577->irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0, irq_flags, 0, irq_chip,
&max14577_irq_chip,
&max14577->irq_data); &max14577->irq_data);
if (ret != 0) { if (ret != 0) {
dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n", dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n",
...@@ -145,8 +363,15 @@ static int max14577_i2c_probe(struct i2c_client *i2c, ...@@ -145,8 +363,15 @@ static int max14577_i2c_probe(struct i2c_client *i2c,
return ret; return ret;
} }
ret = mfd_add_devices(max14577->dev, -1, max14577_devs, /* Max77836 specific initialization code (additional regmap) */
ARRAY_SIZE(max14577_devs), NULL, 0, if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836) {
ret = max77836_init(max14577);
if (ret < 0)
goto err_max77836;
}
ret = mfd_add_devices(max14577->dev, -1, mfd_devs,
mfd_devs_size, NULL, 0,
regmap_irq_get_domain(max14577->irq_data)); regmap_irq_get_domain(max14577->irq_data));
if (ret < 0) if (ret < 0)
goto err_mfd; goto err_mfd;
...@@ -156,6 +381,9 @@ static int max14577_i2c_probe(struct i2c_client *i2c, ...@@ -156,6 +381,9 @@ static int max14577_i2c_probe(struct i2c_client *i2c,
return 0; return 0;
err_mfd: err_mfd:
if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836)
max77836_remove(max14577);
err_max77836:
regmap_del_irq_chip(max14577->irq, max14577->irq_data); regmap_del_irq_chip(max14577->irq, max14577->irq_data);
return ret; return ret;
...@@ -167,12 +395,15 @@ static int max14577_i2c_remove(struct i2c_client *i2c) ...@@ -167,12 +395,15 @@ static int max14577_i2c_remove(struct i2c_client *i2c)
mfd_remove_devices(max14577->dev); mfd_remove_devices(max14577->dev);
regmap_del_irq_chip(max14577->irq, max14577->irq_data); regmap_del_irq_chip(max14577->irq, max14577->irq_data);
if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836)
max77836_remove(max14577);
return 0; return 0;
} }
static const struct i2c_device_id max14577_i2c_id[] = { static const struct i2c_device_id max14577_i2c_id[] = {
{ "max14577", 0 }, { "max14577", MAXIM_DEVICE_TYPE_MAX14577, },
{ "max77836", MAXIM_DEVICE_TYPE_MAX77836, },
{ } { }
}; };
MODULE_DEVICE_TABLE(i2c, max14577_i2c_id); MODULE_DEVICE_TABLE(i2c, max14577_i2c_id);
...@@ -215,11 +446,6 @@ static int max14577_resume(struct device *dev) ...@@ -215,11 +446,6 @@ static int max14577_resume(struct device *dev)
} }
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
static struct of_device_id max14577_dt_match[] = {
{ .compatible = "maxim,max14577", },
{},
};
static SIMPLE_DEV_PM_OPS(max14577_pm, max14577_suspend, max14577_resume); static SIMPLE_DEV_PM_OPS(max14577_pm, max14577_suspend, max14577_resume);
static struct i2c_driver max14577_i2c_driver = { static struct i2c_driver max14577_i2c_driver = {
...@@ -236,6 +462,9 @@ static struct i2c_driver max14577_i2c_driver = { ...@@ -236,6 +462,9 @@ static struct i2c_driver max14577_i2c_driver = {
static int __init max14577_i2c_init(void) static int __init max14577_i2c_init(void)
{ {
BUILD_BUG_ON(ARRAY_SIZE(max14577_i2c_id) != MAXIM_DEVICE_TYPE_NUM);
BUILD_BUG_ON(ARRAY_SIZE(max14577_dt_match) != MAXIM_DEVICE_TYPE_NUM);
return i2c_add_driver(&max14577_i2c_driver); return i2c_add_driver(&max14577_i2c_driver);
} }
subsys_initcall(max14577_i2c_init); subsys_initcall(max14577_i2c_init);
...@@ -247,5 +476,5 @@ static void __exit max14577_i2c_exit(void) ...@@ -247,5 +476,5 @@ static void __exit max14577_i2c_exit(void)
module_exit(max14577_i2c_exit); module_exit(max14577_i2c_exit);
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <k.kozlowski@samsung.com>"); MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <k.kozlowski@samsung.com>");
MODULE_DESCRIPTION("MAXIM 14577 multi-function core driver"); MODULE_DESCRIPTION("Maxim 14577/77836 multi-function core driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -266,11 +266,12 @@ config REGULATOR_LP8788 ...@@ -266,11 +266,12 @@ config REGULATOR_LP8788
This driver supports LP8788 voltage regulator chip. This driver supports LP8788 voltage regulator chip.
config REGULATOR_MAX14577 config REGULATOR_MAX14577
tristate "Maxim 14577 regulator" tristate "Maxim 14577/77836 regulator"
depends on MFD_MAX14577 depends on MFD_MAX14577
help help
This driver controls a Maxim 14577 regulator via I2C bus. This driver controls a Maxim MAX14577/77836 regulator via I2C bus.
The regulators include safeout LDO and current regulator 'CHARGER'. The MAX14577 regulators include safeout LDO and charger current
regulator. The MAX77836 has two additional LDOs.
config REGULATOR_MAX1586 config REGULATOR_MAX1586
tristate "Maxim 1586/1587 voltage regulator" tristate "Maxim 1586/1587 voltage regulator"
......
/* /*
* max14577.c - Regulator driver for the Maxim 14577 * max14577.c - Regulator driver for the Maxim 14577/77836
* *
* Copyright (C) 2013,2014 Samsung Electronics * Copyright (C) 2013,2014 Samsung Electronics
* Krzysztof Kozlowski <k.kozlowski@samsung.com> * Krzysztof Kozlowski <k.kozlowski@samsung.com>
...@@ -22,6 +22,42 @@ ...@@ -22,6 +22,42 @@
#include <linux/mfd/max14577-private.h> #include <linux/mfd/max14577-private.h>
#include <linux/regulator/of_regulator.h> #include <linux/regulator/of_regulator.h>
/*
* Valid limits of current for max14577 and max77836 chargers.
* They must correspond to MBCICHWRCL and MBCICHWRCH fields in CHGCTRL4
* register for given chipset.
*/
struct maxim_charger_current {
/* Minimal current, set in CHGCTRL4/MBCICHWRCL, uA */
unsigned int min;
/*
* Minimal current when high setting is active,
* set in CHGCTRL4/MBCICHWRCH, uA
*/
unsigned int high_start;
/* Value of one step in high setting, uA */
unsigned int high_step;
/* Maximum current of high setting, uA */
unsigned int max;
};
/* Table of valid charger currents for different Maxim chipsets */
static const struct maxim_charger_current maxim_charger_currents[] = {
[MAXIM_DEVICE_TYPE_UNKNOWN] = { 0, 0, 0, 0 },
[MAXIM_DEVICE_TYPE_MAX14577] = {
.min = MAX14577_REGULATOR_CURRENT_LIMIT_MIN,
.high_start = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START,
.high_step = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP,
.max = MAX14577_REGULATOR_CURRENT_LIMIT_MAX,
},
[MAXIM_DEVICE_TYPE_MAX77836] = {
.min = MAX77836_REGULATOR_CURRENT_LIMIT_MIN,
.high_start = MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START,
.high_step = MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP,
.max = MAX77836_REGULATOR_CURRENT_LIMIT_MAX,
},
};
static int max14577_reg_is_enabled(struct regulator_dev *rdev) static int max14577_reg_is_enabled(struct regulator_dev *rdev)
{ {
int rid = rdev_get_id(rdev); int rid = rdev_get_id(rdev);
...@@ -47,6 +83,9 @@ static int max14577_reg_get_current_limit(struct regulator_dev *rdev) ...@@ -47,6 +83,9 @@ static int max14577_reg_get_current_limit(struct regulator_dev *rdev)
{ {
u8 reg_data; u8 reg_data;
struct regmap *rmap = rdev->regmap; struct regmap *rmap = rdev->regmap;
struct max14577 *max14577 = rdev_get_drvdata(rdev);
const struct maxim_charger_current *limits =
&maxim_charger_currents[max14577->dev_type];
if (rdev_get_id(rdev) != MAX14577_CHARGER) if (rdev_get_id(rdev) != MAX14577_CHARGER)
return -EINVAL; return -EINVAL;
...@@ -54,12 +93,11 @@ static int max14577_reg_get_current_limit(struct regulator_dev *rdev) ...@@ -54,12 +93,11 @@ static int max14577_reg_get_current_limit(struct regulator_dev *rdev)
max14577_read_reg(rmap, MAX14577_CHG_REG_CHG_CTRL4, &reg_data); max14577_read_reg(rmap, MAX14577_CHG_REG_CHG_CTRL4, &reg_data);
if ((reg_data & CHGCTRL4_MBCICHWRCL_MASK) == 0) if ((reg_data & CHGCTRL4_MBCICHWRCL_MASK) == 0)
return MAX14577_REGULATOR_CURRENT_LIMIT_MIN; return limits->min;
reg_data = ((reg_data & CHGCTRL4_MBCICHWRCH_MASK) >> reg_data = ((reg_data & CHGCTRL4_MBCICHWRCH_MASK) >>
CHGCTRL4_MBCICHWRCH_SHIFT); CHGCTRL4_MBCICHWRCH_SHIFT);
return MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START + return limits->high_start + reg_data * limits->high_step;
reg_data * MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP;
} }
static int max14577_reg_set_current_limit(struct regulator_dev *rdev, static int max14577_reg_set_current_limit(struct regulator_dev *rdev,
...@@ -67,33 +105,39 @@ static int max14577_reg_set_current_limit(struct regulator_dev *rdev, ...@@ -67,33 +105,39 @@ static int max14577_reg_set_current_limit(struct regulator_dev *rdev,
{ {
int i, current_bits = 0xf; int i, current_bits = 0xf;
u8 reg_data; u8 reg_data;
struct max14577 *max14577 = rdev_get_drvdata(rdev);
const struct maxim_charger_current *limits =
&maxim_charger_currents[max14577->dev_type];
if (rdev_get_id(rdev) != MAX14577_CHARGER) if (rdev_get_id(rdev) != MAX14577_CHARGER)
return -EINVAL; return -EINVAL;
if (min_uA > MAX14577_REGULATOR_CURRENT_LIMIT_MAX || if (min_uA > limits->max || max_uA < limits->min)
max_uA < MAX14577_REGULATOR_CURRENT_LIMIT_MIN)
return -EINVAL; return -EINVAL;
if (max_uA < MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START) { if (max_uA < limits->high_start) {
/* Less than 200 mA, so set 90mA (turn only Low Bit off) */ /*
* Less than high_start,
* so set the minimal current (turn only Low Bit off)
*/
u8 reg_data = 0x0 << CHGCTRL4_MBCICHWRCL_SHIFT; u8 reg_data = 0x0 << CHGCTRL4_MBCICHWRCL_SHIFT;
return max14577_update_reg(rdev->regmap, return max14577_update_reg(rdev->regmap,
MAX14577_CHG_REG_CHG_CTRL4, MAX14577_CHG_REG_CHG_CTRL4,
CHGCTRL4_MBCICHWRCL_MASK, reg_data); CHGCTRL4_MBCICHWRCL_MASK, reg_data);
} }
/* max_uA is in range: <LIMIT_HIGH_START, inifinite>, so search for /*
* valid current starting from LIMIT_MAX. */ * max_uA is in range: <high_start, inifinite>, so search for
for (i = MAX14577_REGULATOR_CURRENT_LIMIT_MAX; * valid current starting from maximum current.
i >= MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START; */
i -= MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP) { for (i = limits->max; i >= limits->high_start; i -= limits->high_step) {
if (i <= max_uA) if (i <= max_uA)
break; break;
current_bits--; current_bits--;
} }
BUG_ON(current_bits < 0); /* Cannot happen */ BUG_ON(current_bits < 0); /* Cannot happen */
/* Turn Low Bit on (use range 200mA-950 mA) */
/* Turn Low Bit on (use range high_start-max)... */
reg_data = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT; reg_data = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT;
/* and set proper High Bits */ /* and set proper High Bits */
reg_data |= current_bits << CHGCTRL4_MBCICHWRCH_SHIFT; reg_data |= current_bits << CHGCTRL4_MBCICHWRCH_SHIFT;
...@@ -118,7 +162,7 @@ static struct regulator_ops max14577_charger_ops = { ...@@ -118,7 +162,7 @@ static struct regulator_ops max14577_charger_ops = {
.set_current_limit = max14577_reg_set_current_limit, .set_current_limit = max14577_reg_set_current_limit,
}; };
static const struct regulator_desc supported_regulators[] = { static const struct regulator_desc max14577_supported_regulators[] = {
[MAX14577_SAFEOUT] = { [MAX14577_SAFEOUT] = {
.name = "SAFEOUT", .name = "SAFEOUT",
.id = MAX14577_SAFEOUT, .id = MAX14577_SAFEOUT,
...@@ -141,16 +185,88 @@ static const struct regulator_desc supported_regulators[] = { ...@@ -141,16 +185,88 @@ static const struct regulator_desc supported_regulators[] = {
}, },
}; };
static struct regulator_ops max77836_ldo_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
/* TODO: add .set_suspend_mode */
};
static const struct regulator_desc max77836_supported_regulators[] = {
[MAX14577_SAFEOUT] = {
.name = "SAFEOUT",
.id = MAX14577_SAFEOUT,
.ops = &max14577_safeout_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.n_voltages = 1,
.min_uV = MAX14577_REGULATOR_SAFEOUT_VOLTAGE,
.enable_reg = MAX14577_REG_CONTROL2,
.enable_mask = CTRL2_SFOUTORD_MASK,
},
[MAX14577_CHARGER] = {
.name = "CHARGER",
.id = MAX14577_CHARGER,
.ops = &max14577_charger_ops,
.type = REGULATOR_CURRENT,
.owner = THIS_MODULE,
.enable_reg = MAX14577_CHG_REG_CHG_CTRL2,
.enable_mask = CHGCTRL2_MBCHOSTEN_MASK,
},
[MAX77836_LDO1] = {
.name = "LDO1",
.id = MAX77836_LDO1,
.ops = &max77836_ldo_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.n_voltages = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM,
.min_uV = MAX77836_REGULATOR_LDO_VOLTAGE_MIN,
.uV_step = MAX77836_REGULATOR_LDO_VOLTAGE_STEP,
.enable_reg = MAX77836_LDO_REG_CNFG1_LDO1,
.enable_mask = MAX77836_CNFG1_LDO_PWRMD_MASK,
.vsel_reg = MAX77836_LDO_REG_CNFG1_LDO1,
.vsel_mask = MAX77836_CNFG1_LDO_TV_MASK,
},
[MAX77836_LDO2] = {
.name = "LDO2",
.id = MAX77836_LDO2,
.ops = &max77836_ldo_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.n_voltages = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM,
.min_uV = MAX77836_REGULATOR_LDO_VOLTAGE_MIN,
.uV_step = MAX77836_REGULATOR_LDO_VOLTAGE_STEP,
.enable_reg = MAX77836_LDO_REG_CNFG1_LDO2,
.enable_mask = MAX77836_CNFG1_LDO_PWRMD_MASK,
.vsel_reg = MAX77836_LDO_REG_CNFG1_LDO2,
.vsel_mask = MAX77836_CNFG1_LDO_TV_MASK,
},
};
#ifdef CONFIG_OF #ifdef CONFIG_OF
static struct of_regulator_match max14577_regulator_matches[] = { static struct of_regulator_match max14577_regulator_matches[] = {
{ .name = "SAFEOUT", }, { .name = "SAFEOUT", },
{ .name = "CHARGER", }, { .name = "CHARGER", },
}; };
static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev) static struct of_regulator_match max77836_regulator_matches[] = {
{ .name = "SAFEOUT", },
{ .name = "CHARGER", },
{ .name = "LDO1", },
{ .name = "LDO2", },
};
static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev,
enum maxim_device_type dev_type)
{ {
int ret; int ret;
struct device_node *np; struct device_node *np;
struct of_regulator_match *regulator_matches;
unsigned int regulator_matches_size;
np = of_get_child_by_name(pdev->dev.parent->of_node, "regulators"); np = of_get_child_by_name(pdev->dev.parent->of_node, "regulators");
if (!np) { if (!np) {
...@@ -158,8 +274,19 @@ static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev) ...@@ -158,8 +274,19 @@ static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
ret = of_regulator_match(&pdev->dev, np, max14577_regulator_matches, switch (dev_type) {
MAX14577_REG_MAX); case MAXIM_DEVICE_TYPE_MAX77836:
regulator_matches = max77836_regulator_matches;
regulator_matches_size = ARRAY_SIZE(max77836_regulator_matches);
break;
case MAXIM_DEVICE_TYPE_MAX14577:
default:
regulator_matches = max14577_regulator_matches;
regulator_matches_size = ARRAY_SIZE(max14577_regulator_matches);
}
ret = of_regulator_match(&pdev->dev, np, regulator_matches,
regulator_matches_size);
if (ret < 0) if (ret < 0)
dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret); dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret);
else else
...@@ -170,31 +297,74 @@ static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev) ...@@ -170,31 +297,74 @@ static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev)
return ret; return ret;
} }
static inline struct regulator_init_data *match_init_data(int index) static inline struct regulator_init_data *match_init_data(int index,
enum maxim_device_type dev_type)
{ {
return max14577_regulator_matches[index].init_data; switch (dev_type) {
case MAXIM_DEVICE_TYPE_MAX77836:
return max77836_regulator_matches[index].init_data;
case MAXIM_DEVICE_TYPE_MAX14577:
default:
return max14577_regulator_matches[index].init_data;
}
} }
static inline struct device_node *match_of_node(int index) static inline struct device_node *match_of_node(int index,
enum maxim_device_type dev_type)
{ {
return max14577_regulator_matches[index].of_node; switch (dev_type) {
case MAXIM_DEVICE_TYPE_MAX77836:
return max77836_regulator_matches[index].of_node;
case MAXIM_DEVICE_TYPE_MAX14577:
default:
return max14577_regulator_matches[index].of_node;
}
} }
#else /* CONFIG_OF */ #else /* CONFIG_OF */
static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev) static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev,
enum maxim_device_type dev_type)
{ {
return 0; return 0;
} }
static inline struct regulator_init_data *match_init_data(int index) static inline struct regulator_init_data *match_init_data(int index,
enum maxim_device_type dev_type)
{ {
return NULL; return NULL;
} }
static inline struct device_node *match_of_node(int index) static inline struct device_node *match_of_node(int index,
enum maxim_device_type dev_type)
{ {
return NULL; return NULL;
} }
#endif /* CONFIG_OF */ #endif /* CONFIG_OF */
/**
* Registers for regulators of max77836 use different I2C slave addresses so
* different regmaps must be used for them.
*
* Returns proper regmap for accessing regulator passed by id.
*/
static struct regmap *max14577_get_regmap(struct max14577 *max14577,
int reg_id)
{
switch (max14577->dev_type) {
case MAXIM_DEVICE_TYPE_MAX77836:
switch (reg_id) {
case MAX77836_SAFEOUT ... MAX77836_CHARGER:
return max14577->regmap;
default:
/* MAX77836_LDO1 ... MAX77836_LDO2 */
return max14577->regmap_pmic;
}
case MAXIM_DEVICE_TYPE_MAX14577:
default:
return max14577->regmap;
}
}
static int max14577_regulator_probe(struct platform_device *pdev) static int max14577_regulator_probe(struct platform_device *pdev)
{ {
...@@ -202,15 +372,29 @@ static int max14577_regulator_probe(struct platform_device *pdev) ...@@ -202,15 +372,29 @@ static int max14577_regulator_probe(struct platform_device *pdev)
struct max14577_platform_data *pdata = dev_get_platdata(max14577->dev); struct max14577_platform_data *pdata = dev_get_platdata(max14577->dev);
int i, ret; int i, ret;
struct regulator_config config = {}; struct regulator_config config = {};
const struct regulator_desc *supported_regulators;
unsigned int supported_regulators_size;
enum maxim_device_type dev_type = max14577->dev_type;
ret = max14577_regulator_dt_parse_pdata(pdev); ret = max14577_regulator_dt_parse_pdata(pdev, dev_type);
if (ret) if (ret)
return ret; return ret;
switch (dev_type) {
case MAXIM_DEVICE_TYPE_MAX77836:
supported_regulators = max77836_supported_regulators;
supported_regulators_size = ARRAY_SIZE(max77836_supported_regulators);
break;
case MAXIM_DEVICE_TYPE_MAX14577:
default:
supported_regulators = max14577_supported_regulators;
supported_regulators_size = ARRAY_SIZE(max14577_supported_regulators);
}
config.dev = &pdev->dev; config.dev = &pdev->dev;
config.regmap = max14577->regmap; config.driver_data = max14577;
for (i = 0; i < ARRAY_SIZE(supported_regulators); i++) { for (i = 0; i < supported_regulators_size; i++) {
struct regulator_dev *regulator; struct regulator_dev *regulator;
/* /*
* Index of supported_regulators[] is also the id and must * Index of supported_regulators[] is also the id and must
...@@ -220,17 +404,19 @@ static int max14577_regulator_probe(struct platform_device *pdev) ...@@ -220,17 +404,19 @@ static int max14577_regulator_probe(struct platform_device *pdev)
config.init_data = pdata->regulators[i].initdata; config.init_data = pdata->regulators[i].initdata;
config.of_node = pdata->regulators[i].of_node; config.of_node = pdata->regulators[i].of_node;
} else { } else {
config.init_data = match_init_data(i); config.init_data = match_init_data(i, dev_type);
config.of_node = match_of_node(i); config.of_node = match_of_node(i, dev_type);
} }
config.regmap = max14577_get_regmap(max14577,
supported_regulators[i].id);
regulator = devm_regulator_register(&pdev->dev, regulator = devm_regulator_register(&pdev->dev,
&supported_regulators[i], &config); &supported_regulators[i], &config);
if (IS_ERR(regulator)) { if (IS_ERR(regulator)) {
ret = PTR_ERR(regulator); ret = PTR_ERR(regulator);
dev_err(&pdev->dev, dev_err(&pdev->dev,
"Regulator init failed for ID %d with error: %d\n", "Regulator init failed for %d/%s with error: %d\n",
i, ret); i, supported_regulators[i].name, ret);
return ret; return ret;
} }
} }
...@@ -238,20 +424,41 @@ static int max14577_regulator_probe(struct platform_device *pdev) ...@@ -238,20 +424,41 @@ static int max14577_regulator_probe(struct platform_device *pdev)
return ret; return ret;
} }
static const struct platform_device_id max14577_regulator_id[] = {
{ "max14577-regulator", MAXIM_DEVICE_TYPE_MAX14577, },
{ "max77836-regulator", MAXIM_DEVICE_TYPE_MAX77836, },
{ }
};
MODULE_DEVICE_TABLE(platform, max14577_regulator_id);
static struct platform_driver max14577_regulator_driver = { static struct platform_driver max14577_regulator_driver = {
.driver = { .driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "max14577-regulator", .name = "max14577-regulator",
}, },
.probe = max14577_regulator_probe, .probe = max14577_regulator_probe,
.id_table = max14577_regulator_id,
}; };
static int __init max14577_regulator_init(void) static int __init max14577_regulator_init(void)
{ {
/* Check for valid values for charger */
BUILD_BUG_ON(MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START + BUILD_BUG_ON(MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START +
MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf != MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf !=
MAX14577_REGULATOR_CURRENT_LIMIT_MAX); MAX14577_REGULATOR_CURRENT_LIMIT_MAX);
BUILD_BUG_ON(ARRAY_SIZE(supported_regulators) != MAX14577_REG_MAX); BUILD_BUG_ON(MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START +
MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf !=
MAX77836_REGULATOR_CURRENT_LIMIT_MAX);
/* Valid charger current values must be provided for each chipset */
BUILD_BUG_ON(ARRAY_SIZE(maxim_charger_currents) != MAXIM_DEVICE_TYPE_NUM);
BUILD_BUG_ON(ARRAY_SIZE(max14577_supported_regulators) != MAX14577_REGULATOR_NUM);
BUILD_BUG_ON(ARRAY_SIZE(max77836_supported_regulators) != MAX77836_REGULATOR_NUM);
BUILD_BUG_ON(MAX77836_REGULATOR_LDO_VOLTAGE_MIN +
(MAX77836_REGULATOR_LDO_VOLTAGE_STEP *
(MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM - 1)) !=
MAX77836_REGULATOR_LDO_VOLTAGE_MAX);
return platform_driver_register(&max14577_regulator_driver); return platform_driver_register(&max14577_regulator_driver);
} }
...@@ -264,6 +471,6 @@ static void __exit max14577_regulator_exit(void) ...@@ -264,6 +471,6 @@ static void __exit max14577_regulator_exit(void)
module_exit(max14577_regulator_exit); module_exit(max14577_regulator_exit);
MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>"); MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>");
MODULE_DESCRIPTION("MAXIM 14577 regulator driver"); MODULE_DESCRIPTION("Maxim 14577/77836 regulator driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:max14577-regulator"); MODULE_ALIAS("platform:max14577-regulator");
...@@ -185,8 +185,21 @@ struct extcon_specific_cable_nb { ...@@ -185,8 +185,21 @@ struct extcon_specific_cable_nb {
*/ */
extern int extcon_dev_register(struct extcon_dev *edev); extern int extcon_dev_register(struct extcon_dev *edev);
extern void extcon_dev_unregister(struct extcon_dev *edev); extern void extcon_dev_unregister(struct extcon_dev *edev);
extern int devm_extcon_dev_register(struct device *dev,
struct extcon_dev *edev);
extern void devm_extcon_dev_unregister(struct device *dev,
struct extcon_dev *edev);
extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name); extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
/*
* Following APIs control the memory of extcon device.
*/
extern struct extcon_dev *extcon_dev_allocate(const char **cables);
extern void extcon_dev_free(struct extcon_dev *edev);
extern struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
const char **cables);
extern void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev);
/* /*
* get/set/update_state access the 32b encoded state value, which represents * get/set/update_state access the 32b encoded state value, which represents
* states of all possible cables of the multistate port. For example, if one * states of all possible cables of the multistate port. For example, if one
...@@ -254,6 +267,30 @@ static inline int extcon_dev_register(struct extcon_dev *edev) ...@@ -254,6 +267,30 @@ static inline int extcon_dev_register(struct extcon_dev *edev)
static inline void extcon_dev_unregister(struct extcon_dev *edev) { } static inline void extcon_dev_unregister(struct extcon_dev *edev) { }
static inline int devm_extcon_dev_register(struct device *dev,
struct extcon_dev *edev)
{
return -EINVAL;
}
static inline void devm_extcon_dev_unregister(struct device *dev,
struct extcon_dev *edev) { }
static inline struct extcon_dev *extcon_dev_allocate(const char **cables)
{
return ERR_PTR(-ENOSYS);
}
static inline void extcon_dev_free(struct extcon_dev *edev) { }
static inline struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
const char **cables)
{
return ERR_PTR(-ENOSYS);
}
static inline void devm_extcon_dev_free(struct extcon_dev *edev) { }
static inline u32 extcon_get_state(struct extcon_dev *edev) static inline u32 extcon_get_state(struct extcon_dev *edev)
{ {
return 0; return 0;
......
/* /*
* max14577-private.h - Common API for the Maxim 14577 internal sub chip * max14577-private.h - Common API for the Maxim 14577/77836 internal sub chip
* *
* Copyright (C) 2013 Samsung Electrnoics * Copyright (C) 2014 Samsung Electrnoics
* Chanwoo Choi <cw00.choi@samsung.com> * Chanwoo Choi <cw00.choi@samsung.com>
* Krzysztof Kozlowski <k.kozlowski@samsung.com> * Krzysztof Kozlowski <k.kozlowski@samsung.com>
* *
...@@ -22,9 +22,19 @@ ...@@ -22,9 +22,19 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#define MAX14577_REG_INVALID (0xff) #define I2C_ADDR_PMIC (0x46 >> 1)
#define I2C_ADDR_MUIC (0x4A >> 1)
#define I2C_ADDR_FG (0x6C >> 1)
/* Slave addr = 0x4A: Interrupt */ enum maxim_device_type {
MAXIM_DEVICE_TYPE_UNKNOWN = 0,
MAXIM_DEVICE_TYPE_MAX14577,
MAXIM_DEVICE_TYPE_MAX77836,
MAXIM_DEVICE_TYPE_NUM,
};
/* Slave addr = 0x4A: MUIC and Charger */
enum max14577_reg { enum max14577_reg {
MAX14577_REG_DEVICEID = 0x00, MAX14577_REG_DEVICEID = 0x00,
MAX14577_REG_INT1 = 0x01, MAX14577_REG_INT1 = 0x01,
...@@ -74,20 +84,22 @@ enum max14577_muic_charger_type { ...@@ -74,20 +84,22 @@ enum max14577_muic_charger_type {
}; };
/* MAX14577 interrupts */ /* MAX14577 interrupts */
#define INT1_ADC_MASK (0x1 << 0) #define MAX14577_INT1_ADC_MASK BIT(0)
#define INT1_ADCLOW_MASK (0x1 << 1) #define MAX14577_INT1_ADCLOW_MASK BIT(1)
#define INT1_ADCERR_MASK (0x1 << 2) #define MAX14577_INT1_ADCERR_MASK BIT(2)
#define MAX77836_INT1_ADC1K_MASK BIT(3)
#define INT2_CHGTYP_MASK (0x1 << 0)
#define INT2_CHGDETRUN_MASK (0x1 << 1) #define MAX14577_INT2_CHGTYP_MASK BIT(0)
#define INT2_DCDTMR_MASK (0x1 << 2) #define MAX14577_INT2_CHGDETRUN_MASK BIT(1)
#define INT2_DBCHG_MASK (0x1 << 3) #define MAX14577_INT2_DCDTMR_MASK BIT(2)
#define INT2_VBVOLT_MASK (0x1 << 4) #define MAX14577_INT2_DBCHG_MASK BIT(3)
#define MAX14577_INT2_VBVOLT_MASK BIT(4)
#define INT3_EOC_MASK (0x1 << 0) #define MAX77836_INT2_VIDRM_MASK BIT(5)
#define INT3_CGMBC_MASK (0x1 << 1)
#define INT3_OVP_MASK (0x1 << 2) #define MAX14577_INT3_EOC_MASK BIT(0)
#define INT3_MBCCHGERR_MASK (0x1 << 3) #define MAX14577_INT3_CGMBC_MASK BIT(1)
#define MAX14577_INT3_OVP_MASK BIT(2)
#define MAX14577_INT3_MBCCHGERR_MASK BIT(3)
/* MAX14577 DEVICE ID register */ /* MAX14577 DEVICE ID register */
#define DEVID_VENDORID_SHIFT 0 #define DEVID_VENDORID_SHIFT 0
...@@ -99,9 +111,11 @@ enum max14577_muic_charger_type { ...@@ -99,9 +111,11 @@ enum max14577_muic_charger_type {
#define STATUS1_ADC_SHIFT 0 #define STATUS1_ADC_SHIFT 0
#define STATUS1_ADCLOW_SHIFT 5 #define STATUS1_ADCLOW_SHIFT 5
#define STATUS1_ADCERR_SHIFT 6 #define STATUS1_ADCERR_SHIFT 6
#define MAX77836_STATUS1_ADC1K_SHIFT 7
#define STATUS1_ADC_MASK (0x1f << STATUS1_ADC_SHIFT) #define STATUS1_ADC_MASK (0x1f << STATUS1_ADC_SHIFT)
#define STATUS1_ADCLOW_MASK (0x1 << STATUS1_ADCLOW_SHIFT) #define STATUS1_ADCLOW_MASK BIT(STATUS1_ADCLOW_SHIFT)
#define STATUS1_ADCERR_MASK (0x1 << STATUS1_ADCERR_SHIFT) #define STATUS1_ADCERR_MASK BIT(STATUS1_ADCERR_SHIFT)
#define MAX77836_STATUS1_ADC1K_MASK BIT(MAX77836_STATUS1_ADC1K_SHIFT)
/* MAX14577 STATUS2 register */ /* MAX14577 STATUS2 register */
#define STATUS2_CHGTYP_SHIFT 0 #define STATUS2_CHGTYP_SHIFT 0
...@@ -109,11 +123,13 @@ enum max14577_muic_charger_type { ...@@ -109,11 +123,13 @@ enum max14577_muic_charger_type {
#define STATUS2_DCDTMR_SHIFT 4 #define STATUS2_DCDTMR_SHIFT 4
#define STATUS2_DBCHG_SHIFT 5 #define STATUS2_DBCHG_SHIFT 5
#define STATUS2_VBVOLT_SHIFT 6 #define STATUS2_VBVOLT_SHIFT 6
#define MAX77836_STATUS2_VIDRM_SHIFT 7
#define STATUS2_CHGTYP_MASK (0x7 << STATUS2_CHGTYP_SHIFT) #define STATUS2_CHGTYP_MASK (0x7 << STATUS2_CHGTYP_SHIFT)
#define STATUS2_CHGDETRUN_MASK (0x1 << STATUS2_CHGDETRUN_SHIFT) #define STATUS2_CHGDETRUN_MASK BIT(STATUS2_CHGDETRUN_SHIFT)
#define STATUS2_DCDTMR_MASK (0x1 << STATUS2_DCDTMR_SHIFT) #define STATUS2_DCDTMR_MASK BIT(STATUS2_DCDTMR_SHIFT)
#define STATUS2_DBCHG_MASK (0x1 << STATUS2_DBCHG_SHIFT) #define STATUS2_DBCHG_MASK BIT(STATUS2_DBCHG_SHIFT)
#define STATUS2_VBVOLT_MASK (0x1 << STATUS2_VBVOLT_SHIFT) #define STATUS2_VBVOLT_MASK BIT(STATUS2_VBVOLT_SHIFT)
#define MAX77836_STATUS2_VIDRM_MASK BIT(MAX77836_STATUS2_VIDRM_SHIFT)
/* MAX14577 CONTROL1 register */ /* MAX14577 CONTROL1 register */
#define COMN1SW_SHIFT 0 #define COMN1SW_SHIFT 0
...@@ -122,8 +138,8 @@ enum max14577_muic_charger_type { ...@@ -122,8 +138,8 @@ enum max14577_muic_charger_type {
#define IDBEN_SHIFT 7 #define IDBEN_SHIFT 7
#define COMN1SW_MASK (0x7 << COMN1SW_SHIFT) #define COMN1SW_MASK (0x7 << COMN1SW_SHIFT)
#define COMP2SW_MASK (0x7 << COMP2SW_SHIFT) #define COMP2SW_MASK (0x7 << COMP2SW_SHIFT)
#define MICEN_MASK (0x1 << MICEN_SHIFT) #define MICEN_MASK BIT(MICEN_SHIFT)
#define IDBEN_MASK (0x1 << IDBEN_SHIFT) #define IDBEN_MASK BIT(IDBEN_SHIFT)
#define CLEAR_IDBEN_MICEN_MASK (COMN1SW_MASK | COMP2SW_MASK) #define CLEAR_IDBEN_MICEN_MASK (COMN1SW_MASK | COMP2SW_MASK)
#define CTRL1_SW_USB ((1 << COMP2SW_SHIFT) \ #define CTRL1_SW_USB ((1 << COMP2SW_SHIFT) \
| (1 << COMN1SW_SHIFT)) | (1 << COMN1SW_SHIFT))
...@@ -143,14 +159,14 @@ enum max14577_muic_charger_type { ...@@ -143,14 +159,14 @@ enum max14577_muic_charger_type {
#define CTRL2_ACCDET_SHIFT (5) #define CTRL2_ACCDET_SHIFT (5)
#define CTRL2_USBCPINT_SHIFT (6) #define CTRL2_USBCPINT_SHIFT (6)
#define CTRL2_RCPS_SHIFT (7) #define CTRL2_RCPS_SHIFT (7)
#define CTRL2_LOWPWR_MASK (0x1 << CTRL2_LOWPWR_SHIFT) #define CTRL2_LOWPWR_MASK BIT(CTRL2_LOWPWR_SHIFT)
#define CTRL2_ADCEN_MASK (0x1 << CTRL2_ADCEN_SHIFT) #define CTRL2_ADCEN_MASK BIT(CTRL2_ADCEN_SHIFT)
#define CTRL2_CPEN_MASK (0x1 << CTRL2_CPEN_SHIFT) #define CTRL2_CPEN_MASK BIT(CTRL2_CPEN_SHIFT)
#define CTRL2_SFOUTASRT_MASK (0x1 << CTRL2_SFOUTASRT_SHIFT) #define CTRL2_SFOUTASRT_MASK BIT(CTRL2_SFOUTASRT_SHIFT)
#define CTRL2_SFOUTORD_MASK (0x1 << CTRL2_SFOUTORD_SHIFT) #define CTRL2_SFOUTORD_MASK BIT(CTRL2_SFOUTORD_SHIFT)
#define CTRL2_ACCDET_MASK (0x1 << CTRL2_ACCDET_SHIFT) #define CTRL2_ACCDET_MASK BIT(CTRL2_ACCDET_SHIFT)
#define CTRL2_USBCPINT_MASK (0x1 << CTRL2_USBCPINT_SHIFT) #define CTRL2_USBCPINT_MASK BIT(CTRL2_USBCPINT_SHIFT)
#define CTRL2_RCPS_MASK (0x1 << CTR2_RCPS_SHIFT) #define CTRL2_RCPS_MASK BIT(CTRL2_RCPS_SHIFT)
#define CTRL2_CPEN1_LOWPWR0 ((1 << CTRL2_CPEN_SHIFT) | \ #define CTRL2_CPEN1_LOWPWR0 ((1 << CTRL2_CPEN_SHIFT) | \
(0 << CTRL2_LOWPWR_SHIFT)) (0 << CTRL2_LOWPWR_SHIFT))
...@@ -198,14 +214,14 @@ enum max14577_charger_reg { ...@@ -198,14 +214,14 @@ enum max14577_charger_reg {
#define CDETCTRL1_DBEXIT_SHIFT 5 #define CDETCTRL1_DBEXIT_SHIFT 5
#define CDETCTRL1_DBIDLE_SHIFT 6 #define CDETCTRL1_DBIDLE_SHIFT 6
#define CDETCTRL1_CDPDET_SHIFT 7 #define CDETCTRL1_CDPDET_SHIFT 7
#define CDETCTRL1_CHGDETEN_MASK (0x1 << CDETCTRL1_CHGDETEN_SHIFT) #define CDETCTRL1_CHGDETEN_MASK BIT(CDETCTRL1_CHGDETEN_SHIFT)
#define CDETCTRL1_CHGTYPMAN_MASK (0x1 << CDETCTRL1_CHGTYPMAN_SHIFT) #define CDETCTRL1_CHGTYPMAN_MASK BIT(CDETCTRL1_CHGTYPMAN_SHIFT)
#define CDETCTRL1_DCDEN_MASK (0x1 << CDETCTRL1_DCDEN_SHIFT) #define CDETCTRL1_DCDEN_MASK BIT(CDETCTRL1_DCDEN_SHIFT)
#define CDETCTRL1_DCD2SCT_MASK (0x1 << CDETCTRL1_DCD2SCT_SHIFT) #define CDETCTRL1_DCD2SCT_MASK BIT(CDETCTRL1_DCD2SCT_SHIFT)
#define CDETCTRL1_DCHKTM_MASK (0x1 << CDETCTRL1_DCHKTM_SHIFT) #define CDETCTRL1_DCHKTM_MASK BIT(CDETCTRL1_DCHKTM_SHIFT)
#define CDETCTRL1_DBEXIT_MASK (0x1 << CDETCTRL1_DBEXIT_SHIFT) #define CDETCTRL1_DBEXIT_MASK BIT(CDETCTRL1_DBEXIT_SHIFT)
#define CDETCTRL1_DBIDLE_MASK (0x1 << CDETCTRL1_DBIDLE_SHIFT) #define CDETCTRL1_DBIDLE_MASK BIT(CDETCTRL1_DBIDLE_SHIFT)
#define CDETCTRL1_CDPDET_MASK (0x1 << CDETCTRL1_CDPDET_SHIFT) #define CDETCTRL1_CDPDET_MASK BIT(CDETCTRL1_CDPDET_SHIFT)
/* MAX14577 CHGCTRL1 register */ /* MAX14577 CHGCTRL1 register */
#define CHGCTRL1_TCHW_SHIFT 4 #define CHGCTRL1_TCHW_SHIFT 4
...@@ -213,9 +229,9 @@ enum max14577_charger_reg { ...@@ -213,9 +229,9 @@ enum max14577_charger_reg {
/* MAX14577 CHGCTRL2 register */ /* MAX14577 CHGCTRL2 register */
#define CHGCTRL2_MBCHOSTEN_SHIFT 6 #define CHGCTRL2_MBCHOSTEN_SHIFT 6
#define CHGCTRL2_MBCHOSTEN_MASK (0x1 << CHGCTRL2_MBCHOSTEN_SHIFT) #define CHGCTRL2_MBCHOSTEN_MASK BIT(CHGCTRL2_MBCHOSTEN_SHIFT)
#define CHGCTRL2_VCHGR_RC_SHIFT 7 #define CHGCTRL2_VCHGR_RC_SHIFT 7
#define CHGCTRL2_VCHGR_RC_MASK (0x1 << CHGCTRL2_VCHGR_RC_SHIFT) #define CHGCTRL2_VCHGR_RC_MASK BIT(CHGCTRL2_VCHGR_RC_SHIFT)
/* MAX14577 CHGCTRL3 register */ /* MAX14577 CHGCTRL3 register */
#define CHGCTRL3_MBCCVWRC_SHIFT 0 #define CHGCTRL3_MBCCVWRC_SHIFT 0
...@@ -225,7 +241,7 @@ enum max14577_charger_reg { ...@@ -225,7 +241,7 @@ enum max14577_charger_reg {
#define CHGCTRL4_MBCICHWRCH_SHIFT 0 #define CHGCTRL4_MBCICHWRCH_SHIFT 0
#define CHGCTRL4_MBCICHWRCH_MASK (0xf << CHGCTRL4_MBCICHWRCH_SHIFT) #define CHGCTRL4_MBCICHWRCH_MASK (0xf << CHGCTRL4_MBCICHWRCH_SHIFT)
#define CHGCTRL4_MBCICHWRCL_SHIFT 4 #define CHGCTRL4_MBCICHWRCL_SHIFT 4
#define CHGCTRL4_MBCICHWRCL_MASK (0x1 << CHGCTRL4_MBCICHWRCL_SHIFT) #define CHGCTRL4_MBCICHWRCL_MASK BIT(CHGCTRL4_MBCICHWRCL_SHIFT)
/* MAX14577 CHGCTRL5 register */ /* MAX14577 CHGCTRL5 register */
#define CHGCTRL5_EOCS_SHIFT 0 #define CHGCTRL5_EOCS_SHIFT 0
...@@ -233,7 +249,7 @@ enum max14577_charger_reg { ...@@ -233,7 +249,7 @@ enum max14577_charger_reg {
/* MAX14577 CHGCTRL6 register */ /* MAX14577 CHGCTRL6 register */
#define CHGCTRL6_AUTOSTOP_SHIFT 5 #define CHGCTRL6_AUTOSTOP_SHIFT 5
#define CHGCTRL6_AUTOSTOP_MASK (0x1 << CHGCTRL6_AUTOSTOP_SHIFT) #define CHGCTRL6_AUTOSTOP_MASK BIT(CHGCTRL6_AUTOSTOP_SHIFT)
/* MAX14577 CHGCTRL7 register */ /* MAX14577 CHGCTRL7 register */
#define CHGCTRL7_OTPCGHCVS_SHIFT 0 #define CHGCTRL7_OTPCGHCVS_SHIFT 0
...@@ -245,14 +261,111 @@ enum max14577_charger_reg { ...@@ -245,14 +261,111 @@ enum max14577_charger_reg {
#define MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP 50000 #define MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP 50000
#define MAX14577_REGULATOR_CURRENT_LIMIT_MAX 950000 #define MAX14577_REGULATOR_CURRENT_LIMIT_MAX 950000
/* MAX77836 regulator current limits (as in CHGCTRL4 register), uA */
#define MAX77836_REGULATOR_CURRENT_LIMIT_MIN 45000
#define MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START 100000
#define MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP 25000
#define MAX77836_REGULATOR_CURRENT_LIMIT_MAX 475000
/* MAX14577 regulator SFOUT LDO voltage, fixed, uV */ /* MAX14577 regulator SFOUT LDO voltage, fixed, uV */
#define MAX14577_REGULATOR_SAFEOUT_VOLTAGE 4900000 #define MAX14577_REGULATOR_SAFEOUT_VOLTAGE 4900000
/* MAX77836 regulator LDOx voltage, uV */
#define MAX77836_REGULATOR_LDO_VOLTAGE_MIN 800000
#define MAX77836_REGULATOR_LDO_VOLTAGE_MAX 3950000
#define MAX77836_REGULATOR_LDO_VOLTAGE_STEP 50000
#define MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM 64
/* Slave addr = 0x46: PMIC */
enum max77836_pmic_reg {
MAX77836_PMIC_REG_PMIC_ID = 0x20,
MAX77836_PMIC_REG_PMIC_REV = 0x21,
MAX77836_PMIC_REG_INTSRC = 0x22,
MAX77836_PMIC_REG_INTSRC_MASK = 0x23,
MAX77836_PMIC_REG_TOPSYS_INT = 0x24,
MAX77836_PMIC_REG_TOPSYS_INT_MASK = 0x26,
MAX77836_PMIC_REG_TOPSYS_STAT = 0x28,
MAX77836_PMIC_REG_MRSTB_CNTL = 0x2A,
MAX77836_PMIC_REG_LSCNFG = 0x2B,
MAX77836_LDO_REG_CNFG1_LDO1 = 0x51,
MAX77836_LDO_REG_CNFG2_LDO1 = 0x52,
MAX77836_LDO_REG_CNFG1_LDO2 = 0x53,
MAX77836_LDO_REG_CNFG2_LDO2 = 0x54,
MAX77836_LDO_REG_CNFG_LDO_BIAS = 0x55,
MAX77836_COMP_REG_COMP1 = 0x60,
MAX77836_PMIC_REG_END,
};
#define MAX77836_INTSRC_MASK_TOP_INT_SHIFT 1
#define MAX77836_INTSRC_MASK_MUIC_CHG_INT_SHIFT 3
#define MAX77836_INTSRC_MASK_TOP_INT_MASK BIT(MAX77836_INTSRC_MASK_TOP_INT_SHIFT)
#define MAX77836_INTSRC_MASK_MUIC_CHG_INT_MASK BIT(MAX77836_INTSRC_MASK_MUIC_CHG_INT_SHIFT)
/* MAX77836 PMIC interrupts */
#define MAX77836_TOPSYS_INT_T120C_SHIFT 0
#define MAX77836_TOPSYS_INT_T140C_SHIFT 1
#define MAX77836_TOPSYS_INT_T120C_MASK BIT(MAX77836_TOPSYS_INT_T120C_SHIFT)
#define MAX77836_TOPSYS_INT_T140C_MASK BIT(MAX77836_TOPSYS_INT_T140C_SHIFT)
/* LDO1/LDO2 CONFIG1 register */
#define MAX77836_CNFG1_LDO_PWRMD_SHIFT 6
#define MAX77836_CNFG1_LDO_TV_SHIFT 0
#define MAX77836_CNFG1_LDO_PWRMD_MASK (0x3 << MAX77836_CNFG1_LDO_PWRMD_SHIFT)
#define MAX77836_CNFG1_LDO_TV_MASK (0x3f << MAX77836_CNFG1_LDO_TV_SHIFT)
/* LDO1/LDO2 CONFIG2 register */
#define MAX77836_CNFG2_LDO_OVCLMPEN_SHIFT 7
#define MAX77836_CNFG2_LDO_ALPMEN_SHIFT 6
#define MAX77836_CNFG2_LDO_COMP_SHIFT 4
#define MAX77836_CNFG2_LDO_POK_SHIFT 3
#define MAX77836_CNFG2_LDO_ADE_SHIFT 1
#define MAX77836_CNFG2_LDO_SS_SHIFT 0
#define MAX77836_CNFG2_LDO_OVCLMPEN_MASK BIT(MAX77836_CNFG2_LDO_OVCLMPEN_SHIFT)
#define MAX77836_CNFG2_LDO_ALPMEN_MASK BIT(MAX77836_CNFG2_LDO_ALPMEN_SHIFT)
#define MAX77836_CNFG2_LDO_COMP_MASK (0x3 << MAX77836_CNFG2_LDO_COMP_SHIFT)
#define MAX77836_CNFG2_LDO_POK_MASK BIT(MAX77836_CNFG2_LDO_POK_SHIFT)
#define MAX77836_CNFG2_LDO_ADE_MASK BIT(MAX77836_CNFG2_LDO_ADE_SHIFT)
#define MAX77836_CNFG2_LDO_SS_MASK BIT(MAX77836_CNFG2_LDO_SS_SHIFT)
/* Slave addr = 0x6C: Fuel-Gauge/Battery */
enum max77836_fg_reg {
MAX77836_FG_REG_VCELL_MSB = 0x02,
MAX77836_FG_REG_VCELL_LSB = 0x03,
MAX77836_FG_REG_SOC_MSB = 0x04,
MAX77836_FG_REG_SOC_LSB = 0x05,
MAX77836_FG_REG_MODE_H = 0x06,
MAX77836_FG_REG_MODE_L = 0x07,
MAX77836_FG_REG_VERSION_MSB = 0x08,
MAX77836_FG_REG_VERSION_LSB = 0x09,
MAX77836_FG_REG_HIBRT_H = 0x0A,
MAX77836_FG_REG_HIBRT_L = 0x0B,
MAX77836_FG_REG_CONFIG_H = 0x0C,
MAX77836_FG_REG_CONFIG_L = 0x0D,
MAX77836_FG_REG_VALRT_MIN = 0x14,
MAX77836_FG_REG_VALRT_MAX = 0x15,
MAX77836_FG_REG_CRATE_MSB = 0x16,
MAX77836_FG_REG_CRATE_LSB = 0x17,
MAX77836_FG_REG_VRESET = 0x18,
MAX77836_FG_REG_FGID = 0x19,
MAX77836_FG_REG_STATUS_H = 0x1A,
MAX77836_FG_REG_STATUS_L = 0x1B,
/*
* TODO: TABLE registers
* TODO: CMD register
*/
MAX77836_FG_REG_END,
};
enum max14577_irq { enum max14577_irq {
/* INT1 */ /* INT1 */
MAX14577_IRQ_INT1_ADC, MAX14577_IRQ_INT1_ADC,
MAX14577_IRQ_INT1_ADCLOW, MAX14577_IRQ_INT1_ADCLOW,
MAX14577_IRQ_INT1_ADCERR, MAX14577_IRQ_INT1_ADCERR,
MAX77836_IRQ_INT1_ADC1K,
/* INT2 */ /* INT2 */
MAX14577_IRQ_INT2_CHGTYP, MAX14577_IRQ_INT2_CHGTYP,
...@@ -260,6 +373,7 @@ enum max14577_irq { ...@@ -260,6 +373,7 @@ enum max14577_irq {
MAX14577_IRQ_INT2_DCDTMR, MAX14577_IRQ_INT2_DCDTMR,
MAX14577_IRQ_INT2_DBCHG, MAX14577_IRQ_INT2_DBCHG,
MAX14577_IRQ_INT2_VBVOLT, MAX14577_IRQ_INT2_VBVOLT,
MAX77836_IRQ_INT2_VIDRM,
/* INT3 */ /* INT3 */
MAX14577_IRQ_INT3_EOC, MAX14577_IRQ_INT3_EOC,
...@@ -267,21 +381,25 @@ enum max14577_irq { ...@@ -267,21 +381,25 @@ enum max14577_irq {
MAX14577_IRQ_INT3_OVP, MAX14577_IRQ_INT3_OVP,
MAX14577_IRQ_INT3_MBCCHGERR, MAX14577_IRQ_INT3_MBCCHGERR,
/* TOPSYS_INT, only MAX77836 */
MAX77836_IRQ_TOPSYS_T140C,
MAX77836_IRQ_TOPSYS_T120C,
MAX14577_IRQ_NUM, MAX14577_IRQ_NUM,
}; };
struct max14577 { struct max14577 {
struct device *dev; struct device *dev;
struct i2c_client *i2c; /* Slave addr = 0x4A */ struct i2c_client *i2c; /* Slave addr = 0x4A */
struct i2c_client *i2c_pmic; /* Slave addr = 0x46 */
enum maxim_device_type dev_type;
struct regmap *regmap; struct regmap *regmap; /* For MUIC and Charger */
struct regmap *regmap_pmic;
struct regmap_irq_chip_data *irq_data; struct regmap_irq_chip_data *irq_data; /* For MUIC and Charger */
struct regmap_irq_chip_data *irq_data_pmic;
int irq; int irq;
/* Device ID */
u8 vendor_id; /* Vendor Identification */
u8 device_id; /* Chip Version */
}; };
/* MAX14577 shared regmap API function */ /* MAX14577 shared regmap API function */
......
/* /*
* max14577.h - Driver for the Maxim 14577 * max14577.h - Driver for the Maxim 14577/77836
* *
* Copyright (C) 2013 Samsung Electrnoics * Copyright (C) 2014 Samsung Electrnoics
* Chanwoo Choi <cw00.choi@samsung.com> * Chanwoo Choi <cw00.choi@samsung.com>
* Krzysztof Kozlowski <k.kozlowski@samsung.com> * Krzysztof Kozlowski <k.kozlowski@samsung.com>
* *
...@@ -20,6 +20,9 @@ ...@@ -20,6 +20,9 @@
* MAX14577 has MUIC, Charger devices. * MAX14577 has MUIC, Charger devices.
* The devices share the same I2C bus and interrupt line * The devices share the same I2C bus and interrupt line
* included in this mfd driver. * included in this mfd driver.
*
* MAX77836 has additional PMIC and Fuel-Gauge on different I2C slave
* addresses.
*/ */
#ifndef __MAX14577_H__ #ifndef __MAX14577_H__
...@@ -32,7 +35,17 @@ enum max14577_regulators { ...@@ -32,7 +35,17 @@ enum max14577_regulators {
MAX14577_SAFEOUT = 0, MAX14577_SAFEOUT = 0,
MAX14577_CHARGER, MAX14577_CHARGER,
MAX14577_REG_MAX, MAX14577_REGULATOR_NUM,
};
/* MAX77836 regulator IDs */
enum max77836_regulators {
MAX77836_SAFEOUT = 0,
MAX77836_CHARGER,
MAX77836_LDO1,
MAX77836_LDO2,
MAX77836_REGULATOR_NUM,
}; };
struct max14577_regulator_platform_data { struct max14577_regulator_platform_data {
......
...@@ -415,7 +415,7 @@ struct palmas_usb { ...@@ -415,7 +415,7 @@ struct palmas_usb {
struct palmas *palmas; struct palmas *palmas;
struct device *dev; struct device *dev;
struct extcon_dev edev; struct extcon_dev *edev;
int id_otg_irq; int id_otg_irq;
int id_irq; int id_irq;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment