You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
601 lines
16 KiB
601 lines
16 KiB
/* |
|
* Copyright (c) 2014 MediaTek Inc. |
|
* Author: HenryC.Chen <henryc.chen@mediatek.com> |
|
* |
|
* This program is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License version 2 as |
|
* published by the Free Software Foundation. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
*/ |
|
#include <linux/err.h> |
|
#include <linux/gpio.h> |
|
#include <linux/i2c.h> |
|
#include <linux/module.h> |
|
#include <linux/init.h> |
|
#include <linux/slab.h> |
|
#include <linux/regulator/driver.h> |
|
#include <linux/regulator/machine.h> |
|
#include <linux/regulator/of_regulator.h> |
|
#include <linux/regmap.h> |
|
#include <linux/irq.h> |
|
#include <linux/interrupt.h> |
|
#include <linux/of.h> |
|
#include <linux/of_device.h> |
|
#include <linux/of_gpio.h> |
|
#include <linux/regulator/da9212-regulator.h> |
|
|
|
#define DA9212_BUCK_MODE_SLEEP 1 |
|
#define DA9212_BUCK_MODE_SYNC 2 |
|
#define DA9212_BUCK_MODE_AUTO 3 |
|
|
|
/* DA9212 REGULATOR IDs */ |
|
#define DA9212_ID_BUCKA 0 |
|
#define DA9212_ID_BUCKB 1 |
|
|
|
#define DA9212_MAX_REGULATORS 2 |
|
#define DA9212_TEST_NODE |
|
|
|
enum da9212_ramp_rate { |
|
RAMP_RATE_2P5MV, |
|
RAMP_RATE_5MV, |
|
RAMP_RATE_10MV, |
|
RAMP_RATE_20MV, |
|
}; |
|
|
|
struct da9212_pdata { |
|
struct device *dev; |
|
int num_buck; |
|
struct regulator_init_data *init_data[DA9212_MAX_REGULATORS]; |
|
int gpio_en[DA9212_MAX_REGULATORS]; |
|
int gpio_vbuck[DA9212_MAX_REGULATORS]; |
|
struct device_node *reg_node[DA9212_MAX_REGULATORS]; |
|
}; |
|
|
|
struct da9212 { |
|
struct device *dev; |
|
struct regmap *regmap; |
|
struct da9212_pdata *pdata; |
|
struct regulator_dev *rdev[DA9212_MAX_REGULATORS]; |
|
int num_regulator; |
|
}; |
|
|
|
static const struct regmap_config da9212_regmap_config = { |
|
.reg_bits = 8, |
|
.val_bits = 8, |
|
}; |
|
|
|
/* Default limits measured in millivolts and milliamps */ |
|
#define DA9212_MIN_MV 300 |
|
#define DA9212_MAX_MV 1570 |
|
#define DA9212_STEP_MV 10 |
|
#define DA9212_RAMP_DELAY 10 |
|
#define DA9212_TURN_ON_DELAY 70 |
|
|
|
/* Current limits for buck (uA) indices corresponds with register values */ |
|
static const int da9212_current_limits[] = { |
|
2000000, 2200000, 2400000, 2600000, 2800000, 3000000, 3200000, 3400000, |
|
3600000, 3800000, 4000000, 4200000, 4400000, 4600000, 4800000, 5000000 |
|
}; |
|
|
|
static unsigned int da9212_buck_get_mode(struct regulator_dev *rdev) |
|
{ |
|
int id = rdev_get_id(rdev); |
|
struct da9212 *chip = rdev_get_drvdata(rdev); |
|
unsigned int data; |
|
int ret, mode; |
|
|
|
ret = regmap_read(chip->regmap, DA9212_REG_BUCKA_CONF + id, &data); |
|
if (ret < 0) |
|
return ret; |
|
mode = 0; |
|
switch (data & DA9212_BUCKA_MODE_MASK) { |
|
case DA9212_BUCK_MODE_SYNC: |
|
mode = REGULATOR_MODE_FAST; |
|
break; |
|
case DA9212_BUCK_MODE_AUTO: |
|
mode = REGULATOR_MODE_NORMAL; |
|
break; |
|
case DA9212_BUCK_MODE_SLEEP: |
|
mode = REGULATOR_MODE_STANDBY; |
|
break; |
|
} |
|
|
|
return mode; |
|
} |
|
|
|
static int da9212_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) |
|
{ |
|
int id = rdev_get_id(rdev); |
|
struct da9212 *chip = rdev_get_drvdata(rdev); |
|
int val; |
|
|
|
val = 0; |
|
switch (mode) { |
|
case REGULATOR_MODE_FAST: |
|
val = DA9212_BUCK_MODE_SYNC; |
|
break; |
|
case REGULATOR_MODE_NORMAL: |
|
val = DA9212_BUCK_MODE_AUTO; |
|
break; |
|
case REGULATOR_MODE_STANDBY: |
|
val = DA9212_BUCK_MODE_SLEEP; |
|
break; |
|
} |
|
|
|
return regmap_update_bits(chip->regmap, DA9212_REG_BUCKA_CONF + id, 0x03, val); |
|
} |
|
|
|
static int da9212_get_current_limit(struct regulator_dev *rdev) |
|
{ |
|
int id = rdev_get_id(rdev); |
|
struct da9212 *chip = rdev_get_drvdata(rdev); |
|
unsigned int data; |
|
int ret; |
|
|
|
ret = regmap_read(chip->regmap, DA9212_REG_BUCK_ILIM, &data); |
|
if (ret < 0) |
|
return ret; |
|
|
|
/* select one of 16 values: 0000 (2000mA) to 1111 (5000mA) */ |
|
data = (data >> id * 4) & 0x0F; |
|
return da9212_current_limits[data]; |
|
} |
|
|
|
static int da9212_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) |
|
{ |
|
int id = rdev_get_id(rdev); |
|
unsigned int ramp_value = DA9212_RAMP_DELAY; |
|
unsigned int mask; |
|
|
|
switch (ramp_delay) { |
|
case 1 ... 2500: |
|
ramp_value = RAMP_RATE_2P5MV; |
|
break; |
|
case 2501 ... 5000: |
|
ramp_value = RAMP_RATE_5MV; |
|
break; |
|
case 5001 ... 10000: |
|
ramp_value = RAMP_RATE_10MV; |
|
break; |
|
case 10001 ... 20000: |
|
ramp_value = RAMP_RATE_20MV; |
|
break; |
|
default: |
|
return -EINVAL; |
|
} |
|
mask = id ? DA9212_SLEW_RATE_B_MASK : DA9212_SLEW_RATE_A_MASK; |
|
return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, mask, ramp_value << 6); |
|
} |
|
|
|
static struct regulator_ops da9212_buck_ops = { |
|
.get_mode = da9212_buck_get_mode, |
|
.set_mode = da9212_buck_set_mode, |
|
.enable = regulator_enable_regmap, |
|
.disable = regulator_disable_regmap, |
|
.is_enabled = regulator_is_enabled_regmap, |
|
.set_voltage_sel = regulator_set_voltage_sel_regmap, |
|
.get_voltage_sel = regulator_get_voltage_sel_regmap, |
|
.set_voltage_time_sel = regulator_set_voltage_time_sel, |
|
.set_ramp_delay = da9212_set_ramp_delay, |
|
.list_voltage = regulator_list_voltage_linear, |
|
.get_current_limit = da9212_get_current_limit, |
|
}; |
|
|
|
#define DA9212_BUCK(_id) \ |
|
{\ |
|
.name = #_id,\ |
|
.ops = &da9212_buck_ops,\ |
|
.type = REGULATOR_VOLTAGE,\ |
|
.id = DA9212_ID_##_id,\ |
|
.n_voltages = (DA9212_MAX_MV - DA9212_MIN_MV) / DA9212_STEP_MV + 1,\ |
|
.min_uV = (DA9212_MIN_MV * 1000),\ |
|
.uV_step = (DA9212_STEP_MV * 1000),\ |
|
.ramp_delay = (DA9212_RAMP_DELAY * 1000), \ |
|
.enable_time = DA9212_TURN_ON_DELAY, \ |
|
.enable_reg = DA9212_REG_BUCKA_CONT + DA9212_ID_##_id,\ |
|
.enable_mask = 1,\ |
|
.vsel_reg = DA9212_REG_VBUCKA_A + DA9212_ID_##_id * 2,\ |
|
.vsel_mask = DA9212_VBUCK_MASK,\ |
|
.owner = THIS_MODULE,\ |
|
} |
|
|
|
static struct regulator_desc da9212_regulators[] = { |
|
DA9212_BUCK(BUCKA), |
|
DA9212_BUCK(BUCKB), |
|
}; |
|
|
|
static int da9212_regulator_init(struct da9212 *chip) |
|
{ |
|
struct regulator_config config = { }; |
|
int i, ret; |
|
unsigned int data; |
|
struct regulation_constraints *c; |
|
|
|
ret = regmap_update_bits(chip->regmap, DA9212_REG_PAGE_CON, |
|
DA9212_REG_PAGE_MASK, DA9212_REG_PAGE2); |
|
if (ret < 0) { |
|
dev_err(chip->dev, "Failed to update PAGE reg: %d\n", ret); |
|
goto err; |
|
} |
|
|
|
ret = regmap_read(chip->regmap, DA9212_REG_INTERFACE, &data); |
|
if (ret < 0 || ((data >> 4) != 0xD)) { |
|
ret = regmap_update_bits(chip->regmap, DA9212_REG_PAGE_CON, |
|
DA9212_REG_PAGE_MASK, DA9212_REG_PAGE0); |
|
dev_err(chip->dev, "Failed to read CONTROL_E reg: %d\n", data); |
|
goto err; |
|
} |
|
|
|
chip->num_regulator = chip->pdata->num_buck; |
|
|
|
ret = regmap_update_bits(chip->regmap, DA9212_REG_PAGE_CON, |
|
DA9212_REG_PAGE_MASK, DA9212_REG_PAGE0); |
|
if (ret < 0) { |
|
dev_err(chip->dev, "Failed to update PAGE reg: %d\n", ret); |
|
goto err; |
|
} |
|
|
|
for (i = 0; i < chip->num_regulator; i++) { |
|
if (chip->pdata->init_data[i]) |
|
config.init_data = chip->pdata->init_data[i]; |
|
config.ena_gpio = 0; /* initialize */ |
|
if (chip->pdata->gpio_en[i]) { |
|
config.ena_gpio = chip->pdata->gpio_en[i]; |
|
config.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH; |
|
} |
|
dev_err(chip->dev, "config.ena_gpio: %d\n", config.ena_gpio); |
|
config.dev = chip->dev; |
|
config.driver_data = chip; |
|
config.of_node = chip->pdata->reg_node[i]; |
|
config.regmap = chip->regmap; |
|
|
|
chip->rdev[i] = regulator_register(&da9212_regulators[i], &config); |
|
if (IS_ERR(chip->rdev[i])) { |
|
dev_err(chip->dev, "Failed to register DA9212 regulator\n"); |
|
ret = PTR_ERR(chip->rdev[i]); |
|
goto err_regulator; |
|
} |
|
/* Constrain board-specific capabilities according to what |
|
* this driver and the chip itself can actually do. |
|
*/ |
|
c = chip->rdev[i]->constraints; |
|
c->valid_modes_mask |= REGULATOR_MODE_NORMAL | |
|
REGULATOR_MODE_STANDBY | REGULATOR_MODE_FAST; |
|
c->valid_ops_mask |= REGULATOR_CHANGE_MODE; |
|
} |
|
|
|
return 0; |
|
|
|
err_regulator: |
|
while (--i >= 0) |
|
regulator_unregister(chip->rdev[i]); |
|
err: |
|
return ret; |
|
} |
|
|
|
/* |
|
* I2C driver interface functions |
|
*/ |
|
static const struct i2c_device_id da9212_i2c_id[] = { |
|
{"da9212-regulator", 0}, |
|
{}, |
|
}; |
|
|
|
#if defined(CONFIG_OF) |
|
static const struct of_device_id da9212_of_match[] = { |
|
{.compatible = "dlg,da9212-regulator", .data = &da9212_i2c_id[0]}, |
|
{}, |
|
}; |
|
|
|
MODULE_DEVICE_TABLE(of, da9212_of_match); |
|
static struct of_regulator_match da9212_matches[] = { |
|
[DA9212_ID_BUCKA] = {.name = "BUCKA"}, |
|
[DA9212_ID_BUCKB] = {.name = "BUCKB"}, |
|
}; |
|
|
|
static struct da9212_pdata *of_get_da9212_platform_data(struct device *dev); |
|
static struct da9212_pdata *of_get_da9212_platform_data(struct device *dev) |
|
{ |
|
struct da9212_pdata *pdata; |
|
struct device_node *node; |
|
enum of_gpio_flags flags; |
|
int num, i, ret; |
|
int cnt; |
|
const struct of_device_id *match; |
|
|
|
match = of_match_device(of_match_ptr(da9212_of_match), dev); |
|
if (!match) { |
|
dev_err(dev, "Error: No device match found\n"); |
|
return NULL; |
|
} |
|
|
|
node = of_get_child_by_name(dev->of_node, "regulators"); |
|
if (!node) { |
|
dev_err(dev, "regulators node not found\n"); |
|
return NULL; |
|
} |
|
|
|
num = of_regulator_match(dev, node, da9212_matches, ARRAY_SIZE(da9212_matches)); |
|
|
|
of_node_put(node); |
|
|
|
if (num < 0) { |
|
dev_err(dev, "Failed to match regulators\n"); |
|
return NULL; |
|
} |
|
|
|
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); |
|
if (!pdata) |
|
return ERR_PTR(-ENOMEM); |
|
|
|
pdata->num_buck = num; |
|
cnt = 0; |
|
|
|
for (i = 0; i < ARRAY_SIZE(da9212_matches); i++) { |
|
if (!da9212_matches[i].init_data) |
|
continue; |
|
|
|
pdata->init_data[cnt] = da9212_matches[i].init_data; |
|
pdata->reg_node[cnt] = da9212_matches[cnt].of_node; |
|
|
|
ret = of_get_named_gpio_flags(da9212_matches[cnt].of_node, |
|
"vbuck-gpio", 0, &flags); |
|
if (ret >= 0) |
|
pdata->gpio_vbuck[cnt] = ret; |
|
ret = of_get_named_gpio_flags(da9212_matches[cnt].of_node, |
|
"en-gpio", 0, &flags); |
|
if (ret >= 0) |
|
pdata->gpio_en[cnt] = ret; |
|
cnt++; |
|
} |
|
dev_info(dev, "pdata->gpio_en : %d, pdata->gpio_vbuck : %d,\n", |
|
pdata->gpio_en[0], pdata->gpio_vbuck[1]); |
|
|
|
return pdata; |
|
} |
|
#else |
|
static struct da9212 *of_get_da9212_platform_data(struct device *dev) |
|
{ |
|
return NULL; |
|
} |
|
#endif |
|
#ifdef DA9212_TEST_NODE |
|
unsigned int reg_value_da9212 = 0; |
|
static ssize_t show_da9212_access(struct device *dev, struct device_attribute *attr, char *buf) |
|
{ |
|
return sprintf(buf, "%x\n", reg_value_da9212); |
|
} |
|
|
|
static ssize_t store_da9212_access(struct device *dev, |
|
struct device_attribute *attr, const char *buf, size_t size) |
|
{ |
|
int ret = 0; |
|
char *pvalue; |
|
char temp_buf[32]; |
|
unsigned long reg_value = 0; |
|
unsigned long reg_address = 0; |
|
struct da9212 *chip = dev_get_drvdata(dev); |
|
|
|
strncpy(temp_buf, buf, sizeof(temp_buf)); |
|
temp_buf[sizeof(temp_buf) - 1] = 0; |
|
pvalue = temp_buf; |
|
|
|
if (size != 0) { |
|
if (size > 4) { |
|
ret = kstrtoul(strsep(&pvalue, " "), 16, ®_address); |
|
if (ret) |
|
return ret; |
|
ret = kstrtoul(pvalue, 16, ®_value); |
|
if (ret) |
|
return ret; |
|
ret = regmap_update_bits(chip->regmap, reg_address, 0xff, reg_value); |
|
if (ret < 0) |
|
dev_err(chip->dev, "Failed to update PAGE reg: %d\n", ret); |
|
|
|
/* restore to page 0,1 */ |
|
ret = regmap_update_bits(chip->regmap, DA9212_REG_PAGE_CON, |
|
DA9212_REG_PAGE_MASK, DA9212_REG_PAGE0); |
|
if (ret < 0) |
|
dev_err(chip->dev, "Failed to update PAGE reg: %d\n", ret); |
|
} else { |
|
ret = kstrtoul(pvalue, 16, ®_address); |
|
if (ret) |
|
return ret; |
|
|
|
ret = regmap_read(chip->regmap, reg_address, ®_value_da9212); |
|
if (ret < 0) |
|
dev_err(chip->dev, "Failed to read reg: %d\n", ret); |
|
|
|
/* restore to page 0,1 */ |
|
ret = regmap_update_bits(chip->regmap, DA9212_REG_PAGE_CON, |
|
DA9212_REG_PAGE_MASK, DA9212_REG_PAGE0); |
|
if (ret < 0) |
|
dev_err(chip->dev, "Failed to update PAGE reg: %d\n", ret); |
|
} |
|
} |
|
return size; |
|
} |
|
|
|
static ssize_t show_da9212_buck_voltage_cpu(struct device *dev, |
|
struct device_attribute *attr, char *buf) |
|
{ |
|
int ret; |
|
struct regulator *reg = devm_regulator_get(dev, "VBUCKA"); |
|
|
|
if (IS_ERR(reg)) { |
|
ret = PTR_ERR(reg); |
|
dev_err(dev, "Failed to request VBUCKA: %d\n", ret); |
|
return 0; |
|
} |
|
ret = sprintf(buf, "%d\n", regulator_get_voltage(reg)); |
|
|
|
devm_regulator_put(reg); |
|
return ret; |
|
} |
|
|
|
static ssize_t store_da9212_buck_voltage_cpu(struct device *dev, |
|
struct device_attribute *attr, const char *buf, |
|
size_t size) |
|
{ |
|
int ret; |
|
unsigned int reg_value = 0; |
|
struct regulator *reg = devm_regulator_get(dev, "VBUCKA"); |
|
|
|
ret = 0; |
|
if (IS_ERR(reg)) { |
|
ret = PTR_ERR(reg); |
|
dev_err(dev, "Failed to request VBUCKA: %d\n", ret); |
|
return ret; |
|
} |
|
if (buf != NULL && size != 0) { |
|
ret = kstrtou32(buf, 0, ®_value); |
|
if (ret) |
|
goto error_input; |
|
/* set voltage */ |
|
ret = regulator_set_voltage(reg, reg_value, reg_value); |
|
if (ret != 0) |
|
dev_err(dev, "Failed to set reg voltage: %d\n", reg_value); |
|
} |
|
error_input: |
|
devm_regulator_put(reg); |
|
return size; |
|
} |
|
|
|
static ssize_t show_da9212_buck_voltage_gpu(struct device *dev, |
|
struct device_attribute *attr, char *buf) |
|
{ |
|
int ret; |
|
struct regulator *reg = devm_regulator_get(dev, "VBUCKB"); |
|
|
|
if (IS_ERR(reg)) { |
|
ret = PTR_ERR(reg); |
|
dev_err(dev, "Failed to request VBUCKB: %d\n", ret); |
|
return 0; |
|
} |
|
ret = sprintf(buf, "%d\n", regulator_get_voltage(reg)); |
|
|
|
devm_regulator_put(reg); |
|
return ret; |
|
} |
|
|
|
static ssize_t store_da9212_buck_voltage_gpu(struct device *dev, |
|
struct device_attribute *attr, const char *buf, |
|
size_t size) |
|
{ |
|
int ret; |
|
unsigned int reg_value = 0; |
|
struct regulator *reg = devm_regulator_get(dev, "VBUCKB"); |
|
|
|
ret = 0; |
|
if (IS_ERR(reg)) { |
|
ret = PTR_ERR(reg); |
|
dev_err(dev, "Failed to request VBUCKB: %d\n", ret); |
|
return ret; |
|
} |
|
if (buf != NULL && size != 0) { |
|
ret = kstrtou32(buf, 0, ®_value); |
|
if (ret) |
|
goto error_input; |
|
/* set voltage */ |
|
ret = regulator_set_voltage(reg, reg_value, reg_value); |
|
if (ret != 0) |
|
dev_err(dev, "Failed to set reg voltage: %d\n", reg_value); |
|
} |
|
error_input: |
|
devm_regulator_put(reg); |
|
return size; |
|
} |
|
|
|
static DEVICE_ATTR(voltage_cpu, 0664, show_da9212_buck_voltage_cpu, store_da9212_buck_voltage_cpu); |
|
static DEVICE_ATTR(voltage_gpu, 0664, show_da9212_buck_voltage_gpu, store_da9212_buck_voltage_gpu); |
|
static DEVICE_ATTR(access, 0664, show_da9212_access, store_da9212_access); |
|
#endif |
|
static int da9212_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) |
|
{ |
|
struct da9212 *chip; |
|
int error, ret; |
|
|
|
chip = devm_kzalloc(&i2c->dev, sizeof(struct da9212), GFP_KERNEL); |
|
|
|
chip->dev = &i2c->dev; |
|
chip->regmap = devm_regmap_init_i2c(i2c, &da9212_regmap_config); |
|
if (IS_ERR(chip->regmap)) { |
|
error = PTR_ERR(chip->regmap); |
|
dev_err(&i2c->dev, "Failed to allocate register map: %d\n", error); |
|
return error; |
|
} |
|
|
|
i2c_set_clientdata(i2c, chip); |
|
|
|
chip->pdata = i2c->dev.platform_data; |
|
if (!chip->pdata && (i2c->dev.of_node)) |
|
chip->pdata = of_get_da9212_platform_data(chip->dev); |
|
if (!chip->pdata) { |
|
dev_err(&i2c->dev, "No platform init data supplied\n"); |
|
return -ENODEV; |
|
} |
|
|
|
ret = da9212_regulator_init(chip); |
|
|
|
if (ret < 0) { |
|
dev_err(&i2c->dev, "Failed to initialize regulator: %d\n", ret); |
|
return ret; |
|
} |
|
dev_err(&i2c->dev, "da9212_i2c_probe: done...\n"); |
|
#ifdef DA9212_TEST_NODE |
|
/* Debug sysfs */ |
|
device_create_file(&(i2c->dev), &dev_attr_voltage_cpu); |
|
device_create_file(&(i2c->dev), &dev_attr_voltage_gpu); |
|
device_create_file(&(i2c->dev), &dev_attr_access); |
|
#endif |
|
return ret; |
|
} |
|
|
|
static int da9212_i2c_remove(struct i2c_client *i2c) |
|
{ |
|
struct da9212 *chip = i2c_get_clientdata(i2c); |
|
int i; |
|
|
|
for (i = 0; i < chip->num_regulator; i++) |
|
regulator_unregister(chip->rdev[i]); |
|
|
|
return 0; |
|
} |
|
|
|
MODULE_DEVICE_TABLE(i2c, da9212_i2c_id); |
|
|
|
static struct i2c_driver da9212_regulator_driver = { |
|
.driver = { |
|
.name = "da9212-regulator", |
|
.owner = THIS_MODULE, |
|
#if defined(CONFIG_OF) |
|
.of_match_table = of_match_ptr(da9212_of_match), |
|
#endif |
|
}, |
|
.probe = da9212_i2c_probe, |
|
.remove = da9212_i2c_remove, |
|
.id_table = da9212_i2c_id, |
|
}; |
|
|
|
static int __init da9212_init(void) |
|
{ |
|
if (i2c_add_driver(&da9212_regulator_driver) != 0) |
|
pr_err("failed to register da9212 i2c driver.\n"); |
|
else |
|
pr_err("Success to register da9212-regulator.\n"); |
|
return 0; |
|
} |
|
subsys_initcall(da9212_init); |
|
|
|
static void __exit da9212_cleanup(void) |
|
{ |
|
i2c_del_driver(&da9212_regulator_driver); |
|
} |
|
module_exit(da9212_cleanup); |
|
|
|
MODULE_DESCRIPTION("Regulator device driver for Dialog DA9212"); |
|
MODULE_LICENSE("GPL v2");
|
|
|