102-mxs-add-regulator-driver.patch 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. --- a/drivers/regulator/Kconfig
  2. +++ b/drivers/regulator/Kconfig
  3. @@ -450,6 +450,14 @@ config REGULATOR_MT6397
  4. This driver supports the control of different power rails of device
  5. through regulator interface.
  6. +config REGULATOR_MXS
  7. + tristate "Freescale MXS on-chip regulators"
  8. + depends on (MXS_POWER || COMPILE_TEST)
  9. + help
  10. + Say y here to support Freescale MXS on-chip regulators.
  11. + It is recommended that this option be enabled on i.MX23,
  12. + i.MX28 platform.
  13. +
  14. config REGULATOR_PALMAS
  15. tristate "TI Palmas PMIC Regulators"
  16. depends on MFD_PALMAS
  17. --- a/drivers/regulator/Makefile
  18. +++ b/drivers/regulator/Makefile
  19. @@ -60,6 +60,7 @@ obj-$(CONFIG_REGULATOR_MC13783) += mc137
  20. obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
  21. obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
  22. obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
  23. +obj-$(CONFIG_REGULATOR_MXS) += mxs-regulator.o
  24. obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
  25. obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
  26. obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
  27. --- /dev/null
  28. +++ b/drivers/regulator/mxs-regulator.c
  29. @@ -0,0 +1,540 @@
  30. +/*
  31. + * Freescale MXS on-chip regulators
  32. + *
  33. + * Embedded Alley Solutions, Inc <source@embeddedalley.com>
  34. + *
  35. + * Copyright (C) 2014 Stefan Wahren
  36. + * Copyright (C) 2010 Freescale Semiconductor, Inc.
  37. + * Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved.
  38. + *
  39. + * Inspired by imx-bootlets
  40. + */
  41. +
  42. +/*
  43. + * The code contained herein is licensed under the GNU General Public
  44. + * License. You may obtain a copy of the GNU General Public License
  45. + * Version 2 or later at the following locations:
  46. + *
  47. + * http://www.opensource.org/licenses/gpl-license.html
  48. + * http://www.gnu.org/copyleft/gpl.html
  49. + */
  50. +
  51. +#include <linux/delay.h>
  52. +#include <linux/device.h>
  53. +#include <linux/err.h>
  54. +#include <linux/kernel.h>
  55. +#include <linux/mfd/syscon.h>
  56. +#include <linux/module.h>
  57. +#include <linux/of.h>
  58. +#include <linux/of_device.h>
  59. +#include <linux/platform_device.h>
  60. +#include <linux/regmap.h>
  61. +#include <linux/regulator/driver.h>
  62. +#include <linux/regulator/machine.h>
  63. +#include <linux/regulator/of_regulator.h>
  64. +#include <linux/slab.h>
  65. +
  66. +/* Powered by linear regulator. DCDC output is gated off and
  67. + the linreg output is equal to the target. */
  68. +#define HW_POWER_LINREG_DCDC_OFF 1
  69. +
  70. +/* Powered by linear regulator. DCDC output is not gated off
  71. + and is ready for the automatic hardware transistion after a 5V
  72. + event. The converters are not enabled when 5V is present. LinReg output
  73. + is 25mV below target. */
  74. +#define HW_POWER_LINREG_DCDC_READY 2
  75. +
  76. +/* Powered by DCDC converter and the LinReg is on. LinReg output
  77. + is 25mV below target. */
  78. +#define HW_POWER_DCDC_LINREG_ON 3
  79. +
  80. +/* Powered by DCDC converter and the LinReg is off. LinReg output
  81. + is 25mV below target. */
  82. +#define HW_POWER_DCDC_LINREG_OFF 4
  83. +
  84. +/* Powered by DCDC converter and the LinReg is ready for the
  85. + automatic hardware transfer. The LinReg output is not enabled and
  86. + depends on the 5V presence to enable the LinRegs. LinReg offset is 25mV
  87. + below target. */
  88. +#define HW_POWER_DCDC_LINREG_READY 5
  89. +
  90. +/* Powered by an external source when 5V is present. This does not
  91. + necessarily mean the external source is powered by 5V,but the chip needs
  92. + to be aware that 5V is present. */
  93. +#define HW_POWER_EXTERNAL_SOURCE_5V 6
  94. +
  95. +/* Powered by an external source when 5V is not present.This doesn't
  96. + necessarily mean the external source is powered by the battery, but the
  97. + chip needs to be aware that the battery is present */
  98. +#define HW_POWER_EXTERNAL_SOURCE_BATTERY 7
  99. +
  100. +/* Unknown configuration. This is an error. */
  101. +#define HW_POWER_UNKNOWN_SOURCE 8
  102. +
  103. +/* TODO: Move power register offsets into header file */
  104. +#define HW_POWER_5VCTRL 0x00000010
  105. +#define HW_POWER_VDDDCTRL 0x00000040
  106. +#define HW_POWER_VDDACTRL 0x00000050
  107. +#define HW_POWER_VDDIOCTRL 0x00000060
  108. +#define HW_POWER_MISC 0x00000090
  109. +#define HW_POWER_STS 0x000000c0
  110. +
  111. +#define BM_POWER_STS_VBUSVALID0_STATUS BIT(15)
  112. +#define BM_POWER_STS_DC_OK BIT(9)
  113. +
  114. +#define BM_POWER_5VCTRL_ILIMIT_EQ_ZERO BIT(2)
  115. +#define BM_POWER_5VCTRL_ENABLE_DCDC BIT(0)
  116. +
  117. +#define BM_POWER_LINREG_OFFSET_DCDC_MODE BIT(1)
  118. +
  119. +#define SHIFT_FREQSEL 4
  120. +
  121. +#define BM_POWER_MISC_FREQSEL (7 << SHIFT_FREQSEL)
  122. +
  123. +/* Recommended DC-DC clock source values */
  124. +#define HW_POWER_MISC_FREQSEL_20000_KHZ 1
  125. +#define HW_POWER_MISC_FREQSEL_24000_KHZ 2
  126. +#define HW_POWER_MISC_FREQSEL_19200_KHZ 3
  127. +
  128. +#define HW_POWER_MISC_SEL_PLLCLK BIT(0)
  129. +
  130. +/* Regulator IDs */
  131. +#define MXS_DCDC 1
  132. +#define MXS_VDDIO 2
  133. +#define MXS_VDDA 3
  134. +#define MXS_VDDD 4
  135. +
  136. +struct mxs_reg_info {
  137. + /* regulator descriptor */
  138. + struct regulator_desc desc;
  139. +
  140. + /* regulator control register */
  141. + int ctrl_reg;
  142. +
  143. + /* disable DC-DC output */
  144. + unsigned int disable_fet_mask;
  145. +
  146. + /* steps between linreg output and DC-DC target */
  147. + unsigned int linreg_offset_mask;
  148. + u8 linreg_offset_shift;
  149. +
  150. + /* function which determine power source */
  151. + u8 (*get_power_source)(struct regulator_dev *);
  152. +};
  153. +
  154. +static inline u8 get_linreg_offset(struct mxs_reg_info *ldo, u32 regs)
  155. +{
  156. + return (regs & ldo->linreg_offset_mask) >> ldo->linreg_offset_shift;
  157. +}
  158. +
  159. +static u8 get_vddio_power_source(struct regulator_dev *reg)
  160. +{
  161. + struct mxs_reg_info *ldo = rdev_get_drvdata(reg);
  162. + u32 v5ctrl, status, base;
  163. + u8 offset;
  164. +
  165. + if (regmap_read(reg->regmap, HW_POWER_5VCTRL, &v5ctrl))
  166. + return HW_POWER_UNKNOWN_SOURCE;
  167. +
  168. + if (regmap_read(reg->regmap, HW_POWER_STS, &status))
  169. + return HW_POWER_UNKNOWN_SOURCE;
  170. +
  171. + if (regmap_read(reg->regmap, ldo->ctrl_reg, &base))
  172. + return HW_POWER_UNKNOWN_SOURCE;
  173. +
  174. + offset = get_linreg_offset(ldo, base);
  175. +
  176. + /* If VBUS valid then 5 V power supply present */
  177. + if (status & BM_POWER_STS_VBUSVALID0_STATUS) {
  178. + /* Powered by Linreg, DC-DC is off */
  179. + if ((base & ldo->disable_fet_mask) &&
  180. + !(offset & BM_POWER_LINREG_OFFSET_DCDC_MODE)) {
  181. + return HW_POWER_LINREG_DCDC_OFF;
  182. + }
  183. +
  184. + if (v5ctrl & BM_POWER_5VCTRL_ENABLE_DCDC) {
  185. + /* Powered by DC-DC, Linreg is on */
  186. + if (offset & BM_POWER_LINREG_OFFSET_DCDC_MODE)
  187. + return HW_POWER_DCDC_LINREG_ON;
  188. + } else {
  189. + /* Powered by Linreg, DC-DC is off */
  190. + if (!(offset & BM_POWER_LINREG_OFFSET_DCDC_MODE))
  191. + return HW_POWER_LINREG_DCDC_OFF;
  192. + }
  193. + } else {
  194. + /* Powered by DC-DC, Linreg is on */
  195. + if (offset & BM_POWER_LINREG_OFFSET_DCDC_MODE)
  196. + return HW_POWER_DCDC_LINREG_ON;
  197. + }
  198. +
  199. + return HW_POWER_UNKNOWN_SOURCE;
  200. +}
  201. +
  202. +static u8 get_vdda_vddd_power_source(struct regulator_dev *reg)
  203. +{
  204. + struct mxs_reg_info *ldo = rdev_get_drvdata(reg);
  205. + struct regulator_desc *desc = &ldo->desc;
  206. + u32 v5ctrl, status, base;
  207. + u8 offset;
  208. +
  209. + if (regmap_read(reg->regmap, HW_POWER_5VCTRL, &v5ctrl))
  210. + return HW_POWER_UNKNOWN_SOURCE;
  211. +
  212. + if (regmap_read(reg->regmap, HW_POWER_STS, &status))
  213. + return HW_POWER_UNKNOWN_SOURCE;
  214. +
  215. + if (regmap_read(reg->regmap, ldo->ctrl_reg, &base))
  216. + return HW_POWER_UNKNOWN_SOURCE;
  217. +
  218. + offset = get_linreg_offset(ldo, base);
  219. +
  220. + /* DC-DC output is disabled */
  221. + if (base & ldo->disable_fet_mask) {
  222. + /* Powered by 5 V supply */
  223. + if (status & BM_POWER_STS_VBUSVALID0_STATUS)
  224. + return HW_POWER_EXTERNAL_SOURCE_5V;
  225. +
  226. + /* Powered by Linreg, DC-DC is off */
  227. + if (!(offset & BM_POWER_LINREG_OFFSET_DCDC_MODE))
  228. + return HW_POWER_LINREG_DCDC_OFF;
  229. + }
  230. +
  231. + /* If VBUS valid then 5 V power supply present */
  232. + if (status & BM_POWER_STS_VBUSVALID0_STATUS) {
  233. + /* Powered by DC-DC, Linreg is on */
  234. + if (v5ctrl & BM_POWER_5VCTRL_ENABLE_DCDC)
  235. + return HW_POWER_DCDC_LINREG_ON;
  236. +
  237. + /* Powered by Linreg, DC-DC is off */
  238. + return HW_POWER_LINREG_DCDC_OFF;
  239. + }
  240. +
  241. + /* DC-DC is on */
  242. + if (offset & BM_POWER_LINREG_OFFSET_DCDC_MODE) {
  243. + /* Powered by DC-DC, Linreg is on */
  244. + if (base & desc->enable_mask)
  245. + return HW_POWER_DCDC_LINREG_ON;
  246. +
  247. + /* Powered by DC-DC, Linreg is off */
  248. + return HW_POWER_DCDC_LINREG_OFF;
  249. + }
  250. +
  251. + return HW_POWER_UNKNOWN_SOURCE;
  252. +}
  253. +
  254. +static int mxs_set_dcdc_freq(struct regulator_dev *reg, u32 hz)
  255. +{
  256. + struct mxs_reg_info *dcdc = rdev_get_drvdata(reg);
  257. + u32 val;
  258. + int ret;
  259. +
  260. + if (dcdc->desc.id != MXS_DCDC) {
  261. + dev_warn(&reg->dev, "Setting switching freq is not supported\n");
  262. + return -EINVAL;
  263. + }
  264. +
  265. + ret = regmap_read(reg->regmap, HW_POWER_MISC, &val);
  266. + if (ret)
  267. + return ret;
  268. +
  269. + val &= ~BM_POWER_MISC_FREQSEL;
  270. + val &= ~HW_POWER_MISC_SEL_PLLCLK;
  271. +
  272. + /*
  273. + * Select the PLL/PFD based frequency that the DC-DC converter uses.
  274. + * The actual switching frequency driving the power inductor is
  275. + * DCDC_CLK/16. Accept only values recommend by Freescale.
  276. + */
  277. + switch (hz) {
  278. + case 1200000:
  279. + val |= HW_POWER_MISC_FREQSEL_19200_KHZ << SHIFT_FREQSEL;
  280. + break;
  281. + case 1250000:
  282. + val |= HW_POWER_MISC_FREQSEL_20000_KHZ << SHIFT_FREQSEL;
  283. + break;
  284. + case 1500000:
  285. + val |= HW_POWER_MISC_FREQSEL_24000_KHZ << SHIFT_FREQSEL;
  286. + break;
  287. + default:
  288. + dev_warn(&reg->dev, "Switching freq: %u Hz not supported\n",
  289. + hz);
  290. + return -EINVAL;
  291. + }
  292. +
  293. + /* First program FREQSEL */
  294. + ret = regmap_write(reg->regmap, HW_POWER_MISC, val);
  295. + if (ret)
  296. + return ret;
  297. +
  298. + /* then set PLL as clock for DC-DC converter */
  299. + val |= HW_POWER_MISC_SEL_PLLCLK;
  300. +
  301. + return regmap_write(reg->regmap, HW_POWER_MISC, val);
  302. +}
  303. +
  304. +static int mxs_ldo_set_voltage_sel(struct regulator_dev *reg, unsigned sel)
  305. +{
  306. + struct mxs_reg_info *ldo = rdev_get_drvdata(reg);
  307. + struct regulator_desc *desc = &ldo->desc;
  308. + u32 status = 0;
  309. + int timeout;
  310. + int ret;
  311. +
  312. + ret = regmap_update_bits(reg->regmap, desc->vsel_reg, desc->vsel_mask,
  313. + sel);
  314. + if (ret)
  315. + return ret;
  316. +
  317. + if (ldo->get_power_source) {
  318. + switch (ldo->get_power_source(reg)) {
  319. + case HW_POWER_LINREG_DCDC_OFF:
  320. + case HW_POWER_LINREG_DCDC_READY:
  321. + case HW_POWER_EXTERNAL_SOURCE_5V:
  322. + /*
  323. + * Since the DC-DC converter is off we can't
  324. + * trigger on DC_OK. So wait at least 1 ms
  325. + * for stabilization.
  326. + */
  327. + usleep_range(1000, 2000);
  328. + return 0;
  329. + }
  330. + }
  331. +
  332. + /* Make sure DC_OK has changed */
  333. + usleep_range(15, 20);
  334. +
  335. + for (timeout = 0; timeout < 20; timeout++) {
  336. + ret = regmap_read(reg->regmap, HW_POWER_STS, &status);
  337. +
  338. + if (ret)
  339. + break;
  340. +
  341. + /* DC-DC converter control loop has stabilized */
  342. + if (status & BM_POWER_STS_DC_OK)
  343. + return 0;
  344. +
  345. + udelay(1);
  346. + }
  347. +
  348. + if (!ret)
  349. + dev_warn_ratelimited(&reg->dev, "%s: timeout status=0x%08x\n",
  350. + __func__, status);
  351. +
  352. + msleep(20);
  353. +
  354. + return -ETIMEDOUT;
  355. +}
  356. +
  357. +static int mxs_ldo_is_enabled(struct regulator_dev *reg)
  358. +{
  359. + struct mxs_reg_info *ldo = rdev_get_drvdata(reg);
  360. +
  361. + if (ldo->get_power_source) {
  362. + switch (ldo->get_power_source(reg)) {
  363. + case HW_POWER_LINREG_DCDC_OFF:
  364. + case HW_POWER_LINREG_DCDC_READY:
  365. + case HW_POWER_DCDC_LINREG_ON:
  366. + return 1;
  367. + }
  368. + }
  369. +
  370. + return 0;
  371. +}
  372. +
  373. +static struct regulator_ops mxs_dcdc_ops = {
  374. + .is_enabled = regulator_is_enabled_regmap,
  375. +};
  376. +
  377. +static struct regulator_ops mxs_ldo_ops = {
  378. + .list_voltage = regulator_list_voltage_linear,
  379. + .map_voltage = regulator_map_voltage_linear,
  380. + .set_voltage_sel = mxs_ldo_set_voltage_sel,
  381. + .get_voltage_sel = regulator_get_voltage_sel_regmap,
  382. + .is_enabled = mxs_ldo_is_enabled,
  383. +};
  384. +
  385. +static const struct mxs_reg_info mxs_info_dcdc = {
  386. + .desc = {
  387. + .name = "dcdc",
  388. + .id = MXS_DCDC,
  389. + .type = REGULATOR_VOLTAGE,
  390. + .owner = THIS_MODULE,
  391. + .ops = &mxs_dcdc_ops,
  392. + .enable_reg = HW_POWER_STS,
  393. + .enable_mask = (1 << 0),
  394. + },
  395. +};
  396. +
  397. +static const struct mxs_reg_info imx23_info_vddio = {
  398. + .desc = {
  399. + .name = "vddio",
  400. + .id = MXS_VDDIO,
  401. + .type = REGULATOR_VOLTAGE,
  402. + .owner = THIS_MODULE,
  403. + .n_voltages = 0x20,
  404. + .uV_step = 25000,
  405. + .linear_min_sel = 0,
  406. + .min_uV = 2800000,
  407. + .vsel_reg = HW_POWER_VDDIOCTRL,
  408. + .vsel_mask = 0x1f,
  409. + .ops = &mxs_ldo_ops,
  410. + },
  411. + .ctrl_reg = HW_POWER_VDDIOCTRL,
  412. + .disable_fet_mask = 1 << 16,
  413. + .linreg_offset_mask = 3 << 12,
  414. + .linreg_offset_shift = 12,
  415. + .get_power_source = get_vddio_power_source,
  416. +};
  417. +
  418. +static const struct mxs_reg_info imx28_info_vddio = {
  419. + .desc = {
  420. + .name = "vddio",
  421. + .id = MXS_VDDIO,
  422. + .type = REGULATOR_VOLTAGE,
  423. + .owner = THIS_MODULE,
  424. + .n_voltages = 0x11,
  425. + .uV_step = 50000,
  426. + .linear_min_sel = 0,
  427. + .min_uV = 2800000,
  428. + .vsel_reg = HW_POWER_VDDIOCTRL,
  429. + .vsel_mask = 0x1f,
  430. + .ops = &mxs_ldo_ops,
  431. + },
  432. + .ctrl_reg = HW_POWER_VDDIOCTRL,
  433. + .disable_fet_mask = 1 << 16,
  434. + .linreg_offset_mask = 3 << 12,
  435. + .linreg_offset_shift = 12,
  436. + .get_power_source = get_vddio_power_source,
  437. +};
  438. +
  439. +static const struct mxs_reg_info mxs_info_vdda = {
  440. + .desc = {
  441. + .name = "vdda",
  442. + .id = MXS_VDDA,
  443. + .type = REGULATOR_VOLTAGE,
  444. + .owner = THIS_MODULE,
  445. + .n_voltages = 0x20,
  446. + .uV_step = 25000,
  447. + .linear_min_sel = 0,
  448. + .min_uV = 1500000,
  449. + .vsel_reg = HW_POWER_VDDACTRL,
  450. + .vsel_mask = 0x1f,
  451. + .ops = &mxs_ldo_ops,
  452. + .enable_mask = (1 << 17),
  453. + },
  454. + .ctrl_reg = HW_POWER_VDDACTRL,
  455. + .disable_fet_mask = 1 << 16,
  456. + .linreg_offset_mask = 3 << 12,
  457. + .linreg_offset_shift = 12,
  458. + .get_power_source = get_vdda_vddd_power_source,
  459. +};
  460. +
  461. +static const struct mxs_reg_info mxs_info_vddd = {
  462. + .desc = {
  463. + .name = "vddd",
  464. + .id = MXS_VDDD,
  465. + .type = REGULATOR_VOLTAGE,
  466. + .owner = THIS_MODULE,
  467. + .n_voltages = 0x20,
  468. + .uV_step = 25000,
  469. + .linear_min_sel = 0,
  470. + .min_uV = 800000,
  471. + .vsel_reg = HW_POWER_VDDDCTRL,
  472. + .vsel_mask = 0x1f,
  473. + .ops = &mxs_ldo_ops,
  474. + .enable_mask = (1 << 21),
  475. + },
  476. + .ctrl_reg = HW_POWER_VDDDCTRL,
  477. + .disable_fet_mask = 1 << 20,
  478. + .linreg_offset_mask = 3 << 16,
  479. + .linreg_offset_shift = 16,
  480. + .get_power_source = get_vdda_vddd_power_source,
  481. +};
  482. +
  483. +static const struct of_device_id of_mxs_regulator_match[] = {
  484. + { .compatible = "fsl,imx23-dcdc", .data = &mxs_info_dcdc },
  485. + { .compatible = "fsl,imx28-dcdc", .data = &mxs_info_dcdc },
  486. + { .compatible = "fsl,imx23-vddio", .data = &imx23_info_vddio },
  487. + { .compatible = "fsl,imx23-vdda", .data = &mxs_info_vdda },
  488. + { .compatible = "fsl,imx23-vddd", .data = &mxs_info_vddd },
  489. + { .compatible = "fsl,imx28-vddio", .data = &imx28_info_vddio },
  490. + { .compatible = "fsl,imx28-vdda", .data = &mxs_info_vdda },
  491. + { .compatible = "fsl,imx28-vddd", .data = &mxs_info_vddd },
  492. + { /* end */ }
  493. +};
  494. +MODULE_DEVICE_TABLE(of, of_mxs_regulator_match);
  495. +
  496. +static int mxs_regulator_probe(struct platform_device *pdev)
  497. +{
  498. + struct device *dev = &pdev->dev;
  499. + const struct of_device_id *match;
  500. + struct device_node *parent_np;
  501. + struct regulator_dev *rdev = NULL;
  502. + struct mxs_reg_info *info;
  503. + struct regulator_init_data *initdata;
  504. + struct regulator_config config = { };
  505. + u32 switch_freq;
  506. +
  507. + match = of_match_device(of_mxs_regulator_match, dev);
  508. + if (!match) {
  509. + /* We do not expect this to happen */
  510. + dev_err(dev, "%s: Unable to match device\n", __func__);
  511. + return -ENODEV;
  512. + }
  513. +
  514. + info = devm_kmemdup(dev, match->data, sizeof(struct mxs_reg_info),
  515. + GFP_KERNEL);
  516. + if (!info)
  517. + return -ENOMEM;
  518. +
  519. + initdata = of_get_regulator_init_data(dev, dev->of_node, &info->desc);
  520. + if (!initdata) {
  521. + dev_err(dev, "missing regulator init data\n");
  522. + return -EINVAL;
  523. + }
  524. +
  525. + parent_np = of_get_parent(dev->of_node);
  526. + if (!parent_np)
  527. + return -ENODEV;
  528. + config.regmap = syscon_node_to_regmap(parent_np);
  529. + of_node_put(parent_np);
  530. + if (IS_ERR(config.regmap))
  531. + return PTR_ERR(config.regmap);
  532. +
  533. + config.dev = dev;
  534. + config.init_data = initdata;
  535. + config.driver_data = info;
  536. + config.of_node = dev->of_node;
  537. +
  538. + rdev = devm_regulator_register(dev, &info->desc, &config);
  539. + if (IS_ERR(rdev)) {
  540. + int ret = PTR_ERR(rdev);
  541. +
  542. + dev_err(dev, "%s: failed to register regulator(%d)\n",
  543. + __func__, ret);
  544. + return ret;
  545. + }
  546. +
  547. + if (!of_property_read_u32(dev->of_node, "switching-frequency",
  548. + &switch_freq))
  549. + mxs_set_dcdc_freq(rdev, switch_freq);
  550. +
  551. + platform_set_drvdata(pdev, rdev);
  552. +
  553. + return 0;
  554. +}
  555. +
  556. +static struct platform_driver mxs_regulator_driver = {
  557. + .driver = {
  558. + .name = "mxs_regulator",
  559. + .of_match_table = of_mxs_regulator_match,
  560. + },
  561. + .probe = mxs_regulator_probe,
  562. +};
  563. +
  564. +module_platform_driver(mxs_regulator_driver);
  565. +
  566. +MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>");
  567. +MODULE_DESCRIPTION("Freescale MXS regulators");
  568. +MODULE_LICENSE("GPL v2");
  569. +MODULE_ALIAS("platform:mxs_regulator");