brintos

brintos / linux-shallow public Read only

0
0
Text · 16.7 KiB · 3e9f8fd Raw
544 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// Regulator driver for ATC260x PMICs4//5// Copyright (C) 2019 Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>6// Copyright (C) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>7 8#include <linux/mfd/atc260x/core.h>9#include <linux/module.h>10#include <linux/of.h>11#include <linux/platform_device.h>12#include <linux/regmap.h>13#include <linux/regulator/driver.h>14 15struct atc260x_regulator_data {16	int voltage_time_dcdc;17	int voltage_time_ldo;18};19 20static const struct linear_range atc2603c_dcdc_voltage_ranges[] = {21	REGULATOR_LINEAR_RANGE(1300000, 0, 13, 50000),22	REGULATOR_LINEAR_RANGE(1950000, 14, 15, 100000),23};24 25static const struct linear_range atc2609a_dcdc_voltage_ranges[] = {26	REGULATOR_LINEAR_RANGE(600000, 0, 127, 6250),27	REGULATOR_LINEAR_RANGE(1400000, 128, 232, 25000),28};29 30static const struct linear_range atc2609a_ldo_voltage_ranges0[] = {31	REGULATOR_LINEAR_RANGE(700000, 0, 15, 100000),32	REGULATOR_LINEAR_RANGE(2100000, 0, 12, 100000),33};34 35static const struct linear_range atc2609a_ldo_voltage_ranges1[] = {36	REGULATOR_LINEAR_RANGE(850000, 0, 15, 100000),37	REGULATOR_LINEAR_RANGE(2100000, 0, 11, 100000),38};39 40static const unsigned int atc260x_ldo_voltage_range_sel[] = {41	0x0, 0x1,42};43 44static int atc260x_dcdc_set_voltage_time_sel(struct regulator_dev *rdev,45					     unsigned int old_selector,46					     unsigned int new_selector)47{48	struct atc260x_regulator_data *data = rdev_get_drvdata(rdev);49 50	if (new_selector > old_selector)51		return data->voltage_time_dcdc;52 53	return 0;54}55 56static int atc260x_ldo_set_voltage_time_sel(struct regulator_dev *rdev,57					    unsigned int old_selector,58					    unsigned int new_selector)59{60	struct atc260x_regulator_data *data = rdev_get_drvdata(rdev);61 62	if (new_selector > old_selector)63		return data->voltage_time_ldo;64 65	return 0;66}67 68static const struct regulator_ops atc260x_dcdc_ops = {69	.enable	= regulator_enable_regmap,70	.disable = regulator_disable_regmap,71	.is_enabled = regulator_is_enabled_regmap,72	.list_voltage = regulator_list_voltage_linear,73	.set_voltage_sel = regulator_set_voltage_sel_regmap,74	.get_voltage_sel = regulator_get_voltage_sel_regmap,75	.set_voltage_time_sel = atc260x_dcdc_set_voltage_time_sel,76};77 78static const struct regulator_ops atc260x_ldo_ops = {79	.enable	= regulator_enable_regmap,80	.disable = regulator_disable_regmap,81	.is_enabled = regulator_is_enabled_regmap,82	.list_voltage = regulator_list_voltage_linear,83	.set_voltage_sel = regulator_set_voltage_sel_regmap,84	.get_voltage_sel = regulator_get_voltage_sel_regmap,85	.set_voltage_time_sel = atc260x_ldo_set_voltage_time_sel,86};87 88static const struct regulator_ops atc260x_ldo_bypass_ops = {89	.enable	= regulator_enable_regmap,90	.disable = regulator_disable_regmap,91	.is_enabled = regulator_is_enabled_regmap,92	.list_voltage = regulator_list_voltage_linear,93	.set_voltage_sel = regulator_set_voltage_sel_regmap,94	.get_voltage_sel = regulator_get_voltage_sel_regmap,95	.set_voltage_time_sel = atc260x_ldo_set_voltage_time_sel,96	.set_bypass = regulator_set_bypass_regmap,97	.get_bypass = regulator_get_bypass_regmap,98};99 100static const struct regulator_ops atc260x_ldo_bypass_discharge_ops = {101	.enable	= regulator_enable_regmap,102	.disable = regulator_disable_regmap,103	.is_enabled = regulator_is_enabled_regmap,104	.list_voltage = regulator_list_voltage_linear,105	.set_voltage_sel = regulator_set_voltage_sel_regmap,106	.get_voltage_sel = regulator_get_voltage_sel_regmap,107	.set_voltage_time_sel = atc260x_ldo_set_voltage_time_sel,108	.set_bypass = regulator_set_bypass_regmap,109	.get_bypass = regulator_get_bypass_regmap,110	.set_active_discharge = regulator_set_active_discharge_regmap,111};112 113static const struct regulator_ops atc260x_dcdc_range_ops = {114	.enable	= regulator_enable_regmap,115	.disable = regulator_disable_regmap,116	.is_enabled = regulator_is_enabled_regmap,117	.list_voltage = regulator_list_voltage_linear_range,118	.set_voltage_sel = regulator_set_voltage_sel_regmap,119	.get_voltage_sel = regulator_get_voltage_sel_regmap,120	.set_voltage_time_sel = atc260x_dcdc_set_voltage_time_sel,121};122 123static const struct regulator_ops atc260x_ldo_range_pick_ops = {124	.enable	= regulator_enable_regmap,125	.disable = regulator_disable_regmap,126	.is_enabled = regulator_is_enabled_regmap,127	.list_voltage = regulator_list_voltage_pickable_linear_range,128	.set_voltage_sel = regulator_set_voltage_sel_pickable_regmap,129	.get_voltage_sel = regulator_get_voltage_sel_pickable_regmap,130	.set_voltage_time_sel = atc260x_ldo_set_voltage_time_sel,131};132 133static const struct regulator_ops atc260x_dcdc_fixed_ops = {134	.list_voltage = regulator_list_voltage_linear,135	.set_voltage_sel = regulator_set_voltage_sel_regmap,136	.get_voltage_sel = regulator_get_voltage_sel_regmap,137	.set_voltage_time_sel = atc260x_dcdc_set_voltage_time_sel,138};139 140static const struct regulator_ops atc260x_ldo_fixed_ops = {141	.list_voltage = regulator_list_voltage_linear,142	.set_voltage_sel = regulator_set_voltage_sel_regmap,143	.get_voltage_sel = regulator_get_voltage_sel_regmap,144	.set_voltage_time_sel = atc260x_ldo_set_voltage_time_sel,145};146 147static const struct regulator_ops atc260x_no_ops = {148};149 150/*151 * Note LDO8 is not documented in datasheet (v2.4), but supported152 * in the vendor's driver implementation (xapp-le-kernel).153 */154enum atc2603c_reg_ids {155	ATC2603C_ID_DCDC1,156	ATC2603C_ID_DCDC2,157	ATC2603C_ID_DCDC3,158	ATC2603C_ID_LDO1,159	ATC2603C_ID_LDO2,160	ATC2603C_ID_LDO3,161	ATC2603C_ID_LDO5,162	ATC2603C_ID_LDO6,163	ATC2603C_ID_LDO7,164	ATC2603C_ID_LDO8,165	ATC2603C_ID_LDO11,166	ATC2603C_ID_LDO12,167	ATC2603C_ID_SWITCHLDO1,168	ATC2603C_ID_MAX,169};170 171#define atc2603c_reg_desc_dcdc(num, min, step, n_volt, vsel_h, vsel_l) { \172	.name = "DCDC"#num, \173	.supply_name = "dcdc"#num, \174	.of_match = of_match_ptr("dcdc"#num), \175	.regulators_node = of_match_ptr("regulators"), \176	.id = ATC2603C_ID_DCDC##num, \177	.ops = &atc260x_dcdc_ops, \178	.type = REGULATOR_VOLTAGE, \179	.min_uV = min, \180	.uV_step = step, \181	.n_voltages = n_volt, \182	.vsel_reg = ATC2603C_PMU_DC##num##_CTL0, \183	.vsel_mask = GENMASK(vsel_h, vsel_l), \184	.enable_reg = ATC2603C_PMU_DC##num##_CTL0, \185	.enable_mask = BIT(15), \186	.enable_time = 800, \187	.owner = THIS_MODULE, \188}189 190#define atc2603c_reg_desc_dcdc_range(num, vsel_h, vsel_l) { \191	.name = "DCDC"#num, \192	.supply_name = "dcdc"#num, \193	.of_match = of_match_ptr("dcdc"#num), \194	.regulators_node = of_match_ptr("regulators"), \195	.id = ATC2603C_ID_DCDC##num, \196	.ops = &atc260x_dcdc_range_ops, \197	.type = REGULATOR_VOLTAGE, \198	.n_voltages = 16, \199	.linear_ranges = atc2603c_dcdc_voltage_ranges, \200	.n_linear_ranges = ARRAY_SIZE(atc2603c_dcdc_voltage_ranges), \201	.vsel_reg = ATC2603C_PMU_DC##num##_CTL0, \202	.vsel_mask = GENMASK(vsel_h, vsel_l), \203	.enable_reg = ATC2603C_PMU_DC##num##_CTL0, \204	.enable_mask = BIT(15), \205	.enable_time = 800, \206	.owner = THIS_MODULE, \207}208 209#define atc2603c_reg_desc_dcdc_fixed(num, min, step, n_volt, vsel_h, vsel_l) { \210	.name = "DCDC"#num, \211	.supply_name = "dcdc"#num, \212	.of_match = of_match_ptr("dcdc"#num), \213	.regulators_node = of_match_ptr("regulators"), \214	.id = ATC2603C_ID_DCDC##num, \215	.ops = &atc260x_dcdc_fixed_ops, \216	.type = REGULATOR_VOLTAGE, \217	.min_uV = min, \218	.uV_step = step, \219	.n_voltages = n_volt, \220	.vsel_reg = ATC2603C_PMU_DC##num##_CTL0, \221	.vsel_mask = GENMASK(vsel_h, vsel_l), \222	.enable_time = 800, \223	.owner = THIS_MODULE, \224}225 226#define atc2603c_reg_desc_ldo(num, min, step, n_volt, vsel_h, vsel_l) { \227	.name = "LDO"#num, \228	.supply_name = "ldo"#num, \229	.of_match = of_match_ptr("ldo"#num), \230	.regulators_node = of_match_ptr("regulators"), \231	.id = ATC2603C_ID_LDO##num, \232	.ops = &atc260x_ldo_ops, \233	.type = REGULATOR_VOLTAGE, \234	.min_uV = min, \235	.uV_step = step, \236	.n_voltages = n_volt, \237	.vsel_reg = ATC2603C_PMU_LDO##num##_CTL, \238	.vsel_mask = GENMASK(vsel_h, vsel_l), \239	.enable_reg = ATC2603C_PMU_LDO##num##_CTL, \240	.enable_mask = BIT(0), \241	.enable_time = 2000, \242	.owner = THIS_MODULE, \243}244 245#define atc2603c_reg_desc_ldo_fixed(num, min, step, n_volt, vsel_h, vsel_l) { \246	.name = "LDO"#num, \247	.supply_name = "ldo"#num, \248	.of_match = of_match_ptr("ldo"#num), \249	.regulators_node = of_match_ptr("regulators"), \250	.id = ATC2603C_ID_LDO##num, \251	.ops = &atc260x_ldo_fixed_ops, \252	.type = REGULATOR_VOLTAGE, \253	.min_uV = min, \254	.uV_step = step, \255	.n_voltages = n_volt, \256	.vsel_reg = ATC2603C_PMU_LDO##num##_CTL, \257	.vsel_mask = GENMASK(vsel_h, vsel_l), \258	.enable_time = 2000, \259	.owner = THIS_MODULE, \260}261 262#define atc2603c_reg_desc_ldo_noops(num, vfixed) { \263	.name = "LDO"#num, \264	.supply_name = "ldo"#num, \265	.of_match = of_match_ptr("ldo"#num), \266	.regulators_node = of_match_ptr("regulators"), \267	.id = ATC2603C_ID_LDO##num, \268	.ops = &atc260x_no_ops, \269	.type = REGULATOR_VOLTAGE, \270	.fixed_uV = vfixed, \271	.n_voltages = 1, \272	.owner = THIS_MODULE, \273}274 275#define atc2603c_reg_desc_ldo_switch(num, min, step, n_volt, vsel_h, vsel_l) { \276	.name = "SWITCHLDO"#num, \277	.supply_name = "switchldo"#num, \278	.of_match = of_match_ptr("switchldo"#num), \279	.regulators_node = of_match_ptr("regulators"), \280	.id = ATC2603C_ID_SWITCHLDO##num, \281	.ops = &atc260x_ldo_bypass_discharge_ops, \282	.type = REGULATOR_VOLTAGE, \283	.min_uV = min, \284	.uV_step = step, \285	.n_voltages = n_volt, \286	.vsel_reg = ATC2603C_PMU_SWITCH_CTL, \287	.vsel_mask = GENMASK(vsel_h, vsel_l), \288	.enable_reg = ATC2603C_PMU_SWITCH_CTL, \289	.enable_mask = BIT(15), \290	.enable_is_inverted = true, \291	.enable_time = 2000, \292	.bypass_reg = ATC2603C_PMU_SWITCH_CTL, \293	.bypass_mask = BIT(5), \294	.active_discharge_reg = ATC2603C_PMU_SWITCH_CTL, \295	.active_discharge_mask = BIT(1), \296	.active_discharge_on = BIT(1), \297	.owner = THIS_MODULE, \298}299 300static const struct regulator_desc atc2603c_reg[] = {301	atc2603c_reg_desc_dcdc_fixed(1, 700000, 25000, 29, 11, 7),302	atc2603c_reg_desc_dcdc_range(2, 12, 8),303	atc2603c_reg_desc_dcdc_fixed(3, 2600000, 100000, 8, 11, 9),304	atc2603c_reg_desc_ldo_fixed(1, 2600000, 100000, 8, 15, 13),305	atc2603c_reg_desc_ldo_fixed(2, 2600000, 100000, 8, 15, 13),306	atc2603c_reg_desc_ldo_fixed(3, 1500000, 100000, 6, 15, 13),307	atc2603c_reg_desc_ldo(5, 2600000, 100000, 8, 15, 13),308	atc2603c_reg_desc_ldo_fixed(6, 700000, 25000, 29, 15, 11),309	atc2603c_reg_desc_ldo(7, 1500000, 100000, 6, 15, 13),310	atc2603c_reg_desc_ldo(8, 2300000, 100000, 11, 15, 12),311	atc2603c_reg_desc_ldo_fixed(11, 2600000, 100000, 8, 15, 13),312	atc2603c_reg_desc_ldo_noops(12, 1800000),313	atc2603c_reg_desc_ldo_switch(1, 3000000, 100000, 4, 4, 3),314};315 316static const struct regulator_desc atc2603c_reg_dcdc2_ver_b =317	atc2603c_reg_desc_dcdc(2, 1000000, 50000, 18, 12, 8);318 319enum atc2609a_reg_ids {320	ATC2609A_ID_DCDC0,321	ATC2609A_ID_DCDC1,322	ATC2609A_ID_DCDC2,323	ATC2609A_ID_DCDC3,324	ATC2609A_ID_DCDC4,325	ATC2609A_ID_LDO0,326	ATC2609A_ID_LDO1,327	ATC2609A_ID_LDO2,328	ATC2609A_ID_LDO3,329	ATC2609A_ID_LDO4,330	ATC2609A_ID_LDO5,331	ATC2609A_ID_LDO6,332	ATC2609A_ID_LDO7,333	ATC2609A_ID_LDO8,334	ATC2609A_ID_LDO9,335	ATC2609A_ID_MAX,336};337 338#define atc2609a_reg_desc_dcdc(num, en_bit) { \339	.name = "DCDC"#num, \340	.supply_name = "dcdc"#num, \341	.of_match = of_match_ptr("dcdc"#num), \342	.regulators_node = of_match_ptr("regulators"), \343	.id = ATC2609A_ID_DCDC##num, \344	.ops = &atc260x_dcdc_ops, \345	.type = REGULATOR_VOLTAGE, \346	.min_uV = 600000, \347	.uV_step = 6250, \348	.n_voltages = 256, \349	.vsel_reg = ATC2609A_PMU_DC##num##_CTL0, \350	.vsel_mask = GENMASK(15, 8), \351	.enable_reg = ATC2609A_PMU_DC_OSC, \352	.enable_mask = BIT(en_bit), \353	.enable_time = 800, \354	.owner = THIS_MODULE, \355}356 357#define atc2609a_reg_desc_dcdc_range(num, en_bit) { \358	.name = "DCDC"#num, \359	.supply_name = "dcdc"#num, \360	.of_match = of_match_ptr("dcdc"#num), \361	.regulators_node = of_match_ptr("regulators"), \362	.id = ATC2609A_ID_DCDC##num, \363	.ops = &atc260x_dcdc_range_ops, \364	.type = REGULATOR_VOLTAGE, \365	.n_voltages = 233, \366	.linear_ranges = atc2609a_dcdc_voltage_ranges, \367	.n_linear_ranges = ARRAY_SIZE(atc2609a_dcdc_voltage_ranges), \368	.vsel_reg = ATC2609A_PMU_DC##num##_CTL0, \369	.vsel_mask = GENMASK(15, 8), \370	.enable_reg = ATC2609A_PMU_DC_OSC, \371	.enable_mask = BIT(en_bit), \372	.enable_time = 800, \373	.owner = THIS_MODULE, \374}375 376#define atc2609a_reg_desc_ldo(num) { \377	.name = "LDO"#num, \378	.supply_name = "ldo"#num, \379	.of_match = of_match_ptr("ldo"#num), \380	.regulators_node = of_match_ptr("regulators"), \381	.id = ATC2609A_ID_LDO##num, \382	.ops = &atc260x_ldo_ops, \383	.type = REGULATOR_VOLTAGE, \384	.min_uV = 700000, \385	.uV_step = 100000, \386	.n_voltages = 16, \387	.vsel_reg = ATC2609A_PMU_LDO##num##_CTL0, \388	.vsel_mask = GENMASK(4, 1), \389	.enable_reg = ATC2609A_PMU_LDO##num##_CTL0, \390	.enable_mask = BIT(0), \391	.enable_time = 2000, \392	.owner = THIS_MODULE, \393}394 395#define atc2609a_reg_desc_ldo_bypass(num) { \396	.name = "LDO"#num, \397	.supply_name = "ldo"#num, \398	.of_match = of_match_ptr("ldo"#num), \399	.regulators_node = of_match_ptr("regulators"), \400	.id = ATC2609A_ID_LDO##num, \401	.ops = &atc260x_ldo_bypass_ops, \402	.type = REGULATOR_VOLTAGE, \403	.min_uV = 2300000, \404	.uV_step = 100000, \405	.n_voltages = 12, \406	.vsel_reg = ATC2609A_PMU_LDO##num##_CTL0, \407	.vsel_mask = GENMASK(5, 2), \408	.enable_reg = ATC2609A_PMU_LDO##num##_CTL0, \409	.enable_mask = BIT(0), \410	.enable_time = 2000, \411	.bypass_reg = ATC2609A_PMU_LDO##num##_CTL0, \412	.bypass_mask = BIT(1), \413	.owner = THIS_MODULE, \414}415 416#define atc2609a_reg_desc_ldo_range_pick(num, n_range, n_volt) { \417	.name = "LDO"#num, \418	.supply_name = "ldo"#num, \419	.of_match = of_match_ptr("ldo"#num), \420	.regulators_node = of_match_ptr("regulators"), \421	.id = ATC2609A_ID_LDO##num, \422	.ops = &atc260x_ldo_range_pick_ops, \423	.type = REGULATOR_VOLTAGE, \424	.linear_ranges = atc2609a_ldo_voltage_ranges##n_range, \425	.n_linear_ranges = ARRAY_SIZE(atc2609a_ldo_voltage_ranges##n_range), \426	.n_voltages = n_volt, \427	.vsel_reg = ATC2609A_PMU_LDO##num##_CTL0, \428	.vsel_mask = GENMASK(4, 1), \429	.vsel_range_reg = ATC2609A_PMU_LDO##num##_CTL0, \430	.vsel_range_mask = BIT(5), \431	.linear_range_selectors_bitfield = atc260x_ldo_voltage_range_sel, \432	.enable_reg = ATC2609A_PMU_LDO##num##_CTL0, \433	.enable_mask = BIT(0), \434	.enable_time = 2000, \435	.owner = THIS_MODULE, \436}437 438#define atc2609a_reg_desc_ldo_fixed(num) { \439	.name = "LDO"#num, \440	.supply_name = "ldo"#num, \441	.of_match = of_match_ptr("ldo"#num), \442	.regulators_node = of_match_ptr("regulators"), \443	.id = ATC2609A_ID_LDO##num, \444	.ops = &atc260x_ldo_fixed_ops, \445	.type = REGULATOR_VOLTAGE, \446	.min_uV = 2600000, \447	.uV_step = 100000, \448	.n_voltages = 8, \449	.vsel_reg = ATC2609A_PMU_LDO##num##_CTL, \450	.vsel_mask = GENMASK(15, 13), \451	.enable_time = 2000, \452	.owner = THIS_MODULE, \453}454 455static const struct regulator_desc atc2609a_reg[] = {456	atc2609a_reg_desc_dcdc(0, 4),457	atc2609a_reg_desc_dcdc(1, 5),458	atc2609a_reg_desc_dcdc(2, 6),459	atc2609a_reg_desc_dcdc_range(3, 7),460	atc2609a_reg_desc_dcdc(4, 8),461	atc2609a_reg_desc_ldo_bypass(0),462	atc2609a_reg_desc_ldo_bypass(1),463	atc2609a_reg_desc_ldo_bypass(2),464	atc2609a_reg_desc_ldo_range_pick(3, 0, 29),465	atc2609a_reg_desc_ldo_range_pick(4, 0, 29),466	atc2609a_reg_desc_ldo(5),467	atc2609a_reg_desc_ldo_range_pick(6, 1, 28),468	atc2609a_reg_desc_ldo_range_pick(7, 0, 29),469	atc2609a_reg_desc_ldo_range_pick(8, 0, 29),470	atc2609a_reg_desc_ldo_fixed(9),471};472 473static int atc260x_regulator_probe(struct platform_device *pdev)474{475	struct atc260x *atc260x = dev_get_drvdata(pdev->dev.parent);476	struct device *dev = atc260x->dev;477	struct atc260x_regulator_data *atc260x_data;478	struct regulator_config config = {};479	struct regulator_dev *atc260x_rdev;480	const struct regulator_desc *regulators;481	bool atc2603c_ver_b = false;482	int i, nregulators;483 484	atc260x_data = devm_kzalloc(&pdev->dev, sizeof(*atc260x_data), GFP_KERNEL);485	if (!atc260x_data)486		return -ENOMEM;487 488	atc260x_data->voltage_time_dcdc = 350;489	atc260x_data->voltage_time_ldo = 800;490 491	switch (atc260x->ic_type) {492	case ATC2603C:493		regulators = atc2603c_reg;494		nregulators = ATC2603C_ID_MAX;495		atc2603c_ver_b = atc260x->ic_ver == ATC260X_B;496		break;497	case ATC2609A:498		atc260x_data->voltage_time_dcdc = 250;499		regulators = atc2609a_reg;500		nregulators = ATC2609A_ID_MAX;501		break;502	default:503		dev_err(dev, "unsupported ATC260X ID %d\n", atc260x->ic_type);504		return -EINVAL;505	}506 507	config.dev = dev;508	config.regmap = atc260x->regmap;509	config.driver_data = atc260x_data;510 511	/* Instantiate the regulators */512	for (i = 0; i < nregulators; i++) {513		if (atc2603c_ver_b && regulators[i].id == ATC2603C_ID_DCDC2)514			atc260x_rdev = devm_regulator_register(&pdev->dev,515							       &atc2603c_reg_dcdc2_ver_b,516							       &config);517		else518			atc260x_rdev = devm_regulator_register(&pdev->dev,519							       &regulators[i],520							       &config);521		if (IS_ERR(atc260x_rdev)) {522			dev_err(dev, "failed to register regulator: %d\n", i);523			return PTR_ERR(atc260x_rdev);524		}525	}526 527	return 0;528}529 530static struct platform_driver atc260x_regulator_driver = {531	.probe = atc260x_regulator_probe,532	.driver = {533		.name = "atc260x-regulator",534		.probe_type = PROBE_PREFER_ASYNCHRONOUS,535	},536};537 538module_platform_driver(atc260x_regulator_driver);539 540MODULE_DESCRIPTION("Regulator driver for ATC260x PMICs");541MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");542MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>");543MODULE_LICENSE("GPL");544