1d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown/*
2d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown * wm831x-isink.c  --  Current sink driver for the WM831x series
3d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown *
4d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown * Copyright 2009 Wolfson Microelectronics PLC.
5d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown *
6d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
7d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown *
8d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown *  This program is free software; you can redistribute  it and/or modify it
9d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown *  under  the terms of  the GNU General  Public License as published by the
10d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown *  Free Software Foundation;  either version 2 of the  License, or (at your
11d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown *  option) any later version.
12d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown */
13d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
14d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown#include <linux/module.h>
15d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown#include <linux/moduleparam.h>
16d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown#include <linux/init.h>
17d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown#include <linux/bitops.h>
18d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown#include <linux/err.h>
19d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown#include <linux/i2c.h>
20d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown#include <linux/platform_device.h>
21d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown#include <linux/regulator/driver.h>
225a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
23d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
24d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown#include <linux/mfd/wm831x/core.h>
25d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown#include <linux/mfd/wm831x/regulator.h>
26d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown#include <linux/mfd/wm831x/pdata.h>
27d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
28d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown#define WM831X_ISINK_MAX_NAME 7
29d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
30d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brownstruct wm831x_isink {
31d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	char name[WM831X_ISINK_MAX_NAME];
32d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	struct regulator_desc desc;
33d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	int reg;
34d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	struct wm831x *wm831x;
35d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	struct regulator_dev *regulator;
36d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown};
37d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
38d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brownstatic int wm831x_isink_enable(struct regulator_dev *rdev)
39d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown{
40d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	struct wm831x_isink *isink = rdev_get_drvdata(rdev);
41d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	struct wm831x *wm831x = isink->wm831x;
42d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	int ret;
43d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
44d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	/* We have a two stage enable: first start the ISINK... */
45d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA,
46d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown			      WM831X_CS1_ENA);
47d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	if (ret != 0)
48d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		return ret;
49d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
50d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	/* ...then enable drive */
51d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_DRIVE,
52d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown			      WM831X_CS1_DRIVE);
53d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	if (ret != 0)
54d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA, 0);
55d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
56d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	return ret;
57d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
58d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown}
59d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
60d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brownstatic int wm831x_isink_disable(struct regulator_dev *rdev)
61d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown{
62d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	struct wm831x_isink *isink = rdev_get_drvdata(rdev);
63d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	struct wm831x *wm831x = isink->wm831x;
64d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	int ret;
65d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
66d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_DRIVE, 0);
67d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	if (ret < 0)
68d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		return ret;
69d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
70d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA, 0);
71d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	if (ret < 0)
72d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		return ret;
73d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
74d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	return ret;
75d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
76d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown}
77d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
78d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brownstatic int wm831x_isink_is_enabled(struct regulator_dev *rdev)
79d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown{
80d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	struct wm831x_isink *isink = rdev_get_drvdata(rdev);
81d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	struct wm831x *wm831x = isink->wm831x;
82d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	int ret;
83d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
84d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	ret = wm831x_reg_read(wm831x, isink->reg);
85d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	if (ret < 0)
86d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		return ret;
87d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
88d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	if ((ret & (WM831X_CS1_ENA | WM831X_CS1_DRIVE)) ==
89d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	    (WM831X_CS1_ENA | WM831X_CS1_DRIVE))
90d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		return 1;
91d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	else
92d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		return 0;
93d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown}
94d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
95d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brownstatic int wm831x_isink_set_current(struct regulator_dev *rdev,
96d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown				    int min_uA, int max_uA)
97d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown{
98d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	struct wm831x_isink *isink = rdev_get_drvdata(rdev);
99d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	struct wm831x *wm831x = isink->wm831x;
100d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	int ret, i;
101d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
102d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	for (i = 0; i < ARRAY_SIZE(wm831x_isinkv_values); i++) {
103d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		int val = wm831x_isinkv_values[i];
104ed3be9a0e3c1050fe07d69a8c600d86cac76cdc4Axel Lin		if (min_uA <= val && val <= max_uA) {
105d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown			ret = wm831x_set_bits(wm831x, isink->reg,
106d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown					      WM831X_CS1_ISEL_MASK, i);
107d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown			return ret;
108d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		}
109d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	}
110d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
111d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	return -EINVAL;
112d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown}
113d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
114d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brownstatic int wm831x_isink_get_current(struct regulator_dev *rdev)
115d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown{
116d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	struct wm831x_isink *isink = rdev_get_drvdata(rdev);
117d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	struct wm831x *wm831x = isink->wm831x;
118d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	int ret;
119d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
120d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	ret = wm831x_reg_read(wm831x, isink->reg);
121d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	if (ret < 0)
122d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		return ret;
123d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
124d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	ret &= WM831X_CS1_ISEL_MASK;
125d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	if (ret > WM831X_ISINK_MAX_ISEL)
126d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		ret = WM831X_ISINK_MAX_ISEL;
127d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
128d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	return wm831x_isinkv_values[ret];
129d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown}
130d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
131d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brownstatic struct regulator_ops wm831x_isink_ops = {
132d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	.is_enabled = wm831x_isink_is_enabled,
133d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	.enable = wm831x_isink_enable,
134d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	.disable = wm831x_isink_disable,
135d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	.set_current_limit = wm831x_isink_set_current,
136d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	.get_current_limit = wm831x_isink_get_current,
137d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown};
138d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
139d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brownstatic irqreturn_t wm831x_isink_irq(int irq, void *data)
140d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown{
141d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	struct wm831x_isink *isink = data;
142d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
143d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	regulator_notifier_call_chain(isink->regulator,
144d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown				      REGULATOR_EVENT_OVER_CURRENT,
145d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown				      NULL);
146d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
147d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	return IRQ_HANDLED;
148d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown}
149d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
150d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
151d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brownstatic __devinit int wm831x_isink_probe(struct platform_device *pdev)
152d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown{
153d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
154d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	struct wm831x_pdata *pdata = wm831x->dev->platform_data;
155d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	struct wm831x_isink *isink;
156d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	int id = pdev->id % ARRAY_SIZE(pdata->isink);
157d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	struct resource *res;
158d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	int ret, irq;
159d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
160d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	dev_dbg(&pdev->dev, "Probing ISINK%d\n", id + 1);
161d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
162d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	if (pdata == NULL || pdata->isink[id] == NULL)
163d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		return -ENODEV;
164d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
165fded2f4faee7670b0545ac05bd2b3ed6b9afcda2Mark Brown	isink = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_isink),
166fded2f4faee7670b0545ac05bd2b3ed6b9afcda2Mark Brown			     GFP_KERNEL);
167d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	if (isink == NULL) {
168d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		dev_err(&pdev->dev, "Unable to allocate private data\n");
169d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		return -ENOMEM;
170d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	}
171d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
172e8092da92e1fd38dc7c38a4eeae93eaa21764584Mark Brown	isink->wm831x = wm831x;
173e8092da92e1fd38dc7c38a4eeae93eaa21764584Mark Brown
174d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
175d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	if (res == NULL) {
176d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		dev_err(&pdev->dev, "No I/O resource\n");
177d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		ret = -EINVAL;
178d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		goto err;
179d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	}
180d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	isink->reg = res->start;
181d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
182d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	/* For current parts this is correct; probably need to revisit
183d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	 * in future.
184d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	 */
185d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	snprintf(isink->name, sizeof(isink->name), "ISINK%d", id + 1);
186d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	isink->desc.name = isink->name;
187d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	isink->desc.id = id;
188d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	isink->desc.ops = &wm831x_isink_ops;
189d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	isink->desc.type = REGULATOR_CURRENT;
190d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	isink->desc.owner = THIS_MODULE;
191d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
192d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	isink->regulator = regulator_register(&isink->desc, &pdev->dev,
1932c043bcbf287dc69848054d5c02c55c20f7a7bc5Rajendra Nayak					     pdata->isink[id], isink, NULL);
194d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	if (IS_ERR(isink->regulator)) {
195d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		ret = PTR_ERR(isink->regulator);
196d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		dev_err(wm831x->dev, "Failed to register ISINK%d: %d\n",
197d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown			id + 1, ret);
198d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		goto err;
199d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	}
200d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
201d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	irq = platform_get_irq(pdev, 0);
202dfda9c27ba15330f37453c389d775ecf9e981d05Mark Brown	ret = request_threaded_irq(irq, NULL, wm831x_isink_irq,
203dfda9c27ba15330f37453c389d775ecf9e981d05Mark Brown				   IRQF_TRIGGER_RISING, isink->name, isink);
204d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	if (ret != 0) {
205d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		dev_err(&pdev->dev, "Failed to request ISINK IRQ %d: %d\n",
206d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown			irq, ret);
207d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		goto err_regulator;
208d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	}
209d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
210d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	platform_set_drvdata(pdev, isink);
211d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
212d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	return 0;
213d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
214d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brownerr_regulator:
215d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	regulator_unregister(isink->regulator);
216d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brownerr:
217d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	return ret;
218d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown}
219d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
220d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brownstatic __devexit int wm831x_isink_remove(struct platform_device *pdev)
221d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown{
222d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	struct wm831x_isink *isink = platform_get_drvdata(pdev);
223d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
224eb66d565e8eb73ab0c471fd3dac822dc663fe8eaDmitry Torokhov	platform_set_drvdata(pdev, NULL);
225eb66d565e8eb73ab0c471fd3dac822dc663fe8eaDmitry Torokhov
226dfda9c27ba15330f37453c389d775ecf9e981d05Mark Brown	free_irq(platform_get_irq(pdev, 0), isink);
227d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
228d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	regulator_unregister(isink->regulator);
229d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
230d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	return 0;
231d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown}
232d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
233d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brownstatic struct platform_driver wm831x_isink_driver = {
234d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	.probe = wm831x_isink_probe,
235d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	.remove = __devexit_p(wm831x_isink_remove),
236d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	.driver		= {
237d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		.name	= "wm831x-isink",
238eb66d565e8eb73ab0c471fd3dac822dc663fe8eaDmitry Torokhov		.owner	= THIS_MODULE,
239d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	},
240d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown};
241d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
242d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brownstatic int __init wm831x_isink_init(void)
243d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown{
244d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	int ret;
245d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	ret = platform_driver_register(&wm831x_isink_driver);
246d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	if (ret != 0)
247d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown		pr_err("Failed to register WM831x ISINK driver: %d\n", ret);
248d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
249d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	return ret;
250d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown}
251d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brownsubsys_initcall(wm831x_isink_init);
252d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
253d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brownstatic void __exit wm831x_isink_exit(void)
254d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown{
255d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown	platform_driver_unregister(&wm831x_isink_driver);
256d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown}
257d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brownmodule_exit(wm831x_isink_exit);
258d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown
259d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark Brown/* Module information */
260d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark BrownMODULE_AUTHOR("Mark Brown");
261d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark BrownMODULE_DESCRIPTION("WM831x current sink driver");
262d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark BrownMODULE_LICENSE("GPL");
263d4d6b722e780f005f0d4e43a43909fa51cc33a11Mark BrownMODULE_ALIAS("platform:wm831x-isink");
264