/*
* wm8350.c -- Voltage and current regulation for the Wolfson WM8350 PMIC
*
* Copyright 2007, 2008 Wolfson Microelectronics PLC.
*
* Author: Liam Girdwood
* linux@wolfsonmicro.com
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/mfd/wm8350/core.h>
#include <linux/mfd/wm8350/pmic.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
/* Maximum value possible for VSEL */
#define WM8350_DCDC_MAX_VSEL 0x66
/* Microamps */
static const int isink_cur[] = {
4,
5,
6,
7,
8,
10,
11,
14,
16,
19,
23,
27,
32,
39,
46,
54,
65,
77,
92,
109,
130,
154,
183,
218,
259,
308,
367,
436,
518,
616,
733,
872,
1037,
1233,
1466,
1744,
2073,
2466,
2933,
3487,
4147,
4932,
5865,
6975,
8294,
9864,
11730,
13949,
16589,
19728,
23460,
27899,
33178,
39455,
46920,
55798,
66355,
78910,
93840,
111596,
132710,
157820,
187681,
223191
};
static int get_isink_val(int min_uA, int max_uA, u16 *setting)
{
int i;
for (i = ARRAY_SIZE(isink_cur) - 1; i >= 0; i--) {
if (min_uA <= isink_cur[i] && max_uA >= isink_cur[i]) {
*setting = i;
return 0;
}
}
return -EINVAL;
}
static inline int wm8350_ldo_val_to_mvolts(unsigned int val)
{
if (val < 16)
return (val * 50) + 900;
else
return ((val - 16) * 100) + 1800;
}
static inline unsigned int wm8350_ldo_mvolts_to_val(int mV)
{
if (mV < 1800)
return (mV - 900) / 50;
else
return ((mV - 1800) / 100) + 16;
}
static inline int wm8350_dcdc_val_to_mvolts(unsigned int val)
{
return (val * 25) + 850;
}
static inline unsigned int wm8350_dcdc_mvolts_to_val(int mV)
{
return (mV - 850) / 25;
}
static int wm8350_isink_set_current(struct regulator_dev *rdev, int min_uA,
int max_uA)
{
struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
int isink = rdev_get_id(rdev);
u16 val, setting;
int ret;
ret = get_isink_val(min_uA, max_uA, &setting);
if (ret != 0)
return ret;
switch (isink) {
case WM8350_ISINK_A:
val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) &
~WM8350_CS1_ISEL_MASK;
wm8350_reg_write(wm8350, WM8350_CURRENT_SINK_DRIVER_A,
val | setting);
break;
case WM8350_ISINK_B:
val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) &
~WM8350_CS1_ISEL_MASK;
wm8350_reg_write(wm8350, WM8350_CURRENT_SINK_DRIVER_B,
val | setting);
break;
default:
return -EINVAL;
}
return 0;
}
static int wm8350_isink_get_current(struct regulator_dev *rdev)
{
struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
int isink = rdev_get_id(rdev);
u16 val;
switch (isink) {
case WM8350_ISINK_A:
val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) &
WM8350_CS1_ISEL_MASK;
break;
case WM8350_ISINK_B:
val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) &
WM8350_CS1_ISEL_MASK;
break;
default:
return 0;
}
return (isink_cur[val] + 50) / 100;
}
/* turn on ISINK followed by DCDC */
static int wm8350_isink_enable(struct regulator_dev *rdev)
{
struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
int isink = rdev_get_id(rdev);
switch (isink) {
case WM8350_ISINK_A:
switch (wm8350->pmic.isink_A_dcdc) {
case WM8350_DCDC_2:
case WM8350_DCDC_5:
wm8350_set_bits(wm8350, WM8350_POWER_MGMT_7,
WM8350_CS1_ENA);
wm8350_set_bits(wm8350, WM8350_CSA_FLASH_CONTROL,
WM8350_CS1_DRIVE);
wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED,
1 << (wm8350->pmic.isink_A_dcdc -
WM8350_DCDC_1));
break;
default:
return -EINVAL;
}
|