Commit | Line | Data |
---|---|---|
a26c20b1 ML |
1 | /* |
2 | * I2C bus driver for the SH7760 I2C Interfaces. | |
3 | * | |
4 | * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau@msc-ge.com> | |
5 | * | |
6 | * licensed under the terms outlined in the file COPYING. | |
7 | * | |
8 | */ | |
9 | ||
10 | #include <linux/completion.h> | |
11 | #include <linux/delay.h> | |
12 | #include <linux/err.h> | |
13 | #include <linux/i2c.h> | |
a26c20b1 ML |
14 | #include <linux/interrupt.h> |
15 | #include <linux/ioport.h> | |
16 | #include <linux/platform_device.h> | |
17 | #include <linux/slab.h> | |
21782180 | 18 | #include <linux/io.h> |
93cf5d75 | 19 | #include <linux/module.h> |
a26c20b1 ML |
20 | |
21 | #include <asm/clock.h> | |
22 | #include <asm/i2c-sh7760.h> | |
a26c20b1 ML |
23 | |
24 | /* register offsets */ | |
25 | #define I2CSCR 0x0 /* slave ctrl */ | |
26 | #define I2CMCR 0x4 /* master ctrl */ | |
27 | #define I2CSSR 0x8 /* slave status */ | |
28 | #define I2CMSR 0xC /* master status */ | |
29 | #define I2CSIER 0x10 /* slave irq enable */ | |
30 | #define I2CMIER 0x14 /* master irq enable */ | |
31 | #define I2CCCR 0x18 /* clock dividers */ | |
32 | #define I2CSAR 0x1c /* slave address */ | |
33 | #define I2CMAR 0x20 /* master address */ | |
34 | #define I2CRXTX 0x24 /* data port */ | |
35 | #define I2CFCR 0x28 /* fifo control */ | |
36 | #define I2CFSR 0x2C /* fifo status */ | |
37 | #define I2CFIER 0x30 /* fifo irq enable */ | |
38 | #define I2CRFDR 0x34 /* rx fifo count */ | |
39 | #define I2CTFDR 0x38 /* tx fifo count */ | |
40 | ||
41 | #define REGSIZE 0x3C | |
42 | ||
43 | #define MCR_MDBS 0x80 /* non-fifo mode switch */ | |
44 | #define MCR_FSCL 0x40 /* override SCL pin */ | |
45 | #define MCR_FSDA 0x20 /* override SDA pin */ | |
46 | #define MCR_OBPC 0x10 /* override pins */ | |
47 | #define MCR_MIE 0x08 /* master if enable */ | |
48 | #define MCR_TSBE 0x04 | |
49 | #define MCR_FSB 0x02 /* force stop bit */ | |
50 | #define MCR_ESG 0x01 /* en startbit gen. */ | |
51 | ||
52 | #define MSR_MNR 0x40 /* nack received */ | |
53 | #define MSR_MAL 0x20 /* arbitration lost */ | |
54 | #define MSR_MST 0x10 /* sent a stop */ | |
55 | #define MSR_MDE 0x08 | |
56 | #define MSR_MDT 0x04 | |
57 | #define MSR_MDR 0x02 | |
58 | #define MSR_MAT 0x01 /* slave addr xfer done */ | |
59 | ||
60 | #define MIE_MNRE 0x40 /* nack irq en */ | |
61 | #define MIE_MALE 0x20 /* arblos irq en */ | |
62 | #define MIE_MSTE 0x10 /* stop irq en */ | |
63 | #define MIE_MDEE 0x08 | |
64 | #define MIE_MDTE 0x04 | |
65 | #define MIE_MDRE 0x02 | |
66 | #define MIE_MATE 0x01 /* address sent irq en */ | |
67 | ||
68 | #define FCR_RFRST 0x02 /* reset rx fifo */ | |
69 | #define FCR_TFRST 0x01 /* reset tx fifo */ | |
70 | ||
71 | #define FSR_TEND 0x04 /* last byte sent */ | |
72 | #define FSR_RDF 0x02 /* rx fifo trigger */ | |
73 | #define FSR_TDFE 0x01 /* tx fifo empty */ | |
74 | ||
75 | #define FIER_TEIE 0x04 /* tx fifo empty irq en */ | |
76 | #define FIER_RXIE 0x02 /* rx fifo trig irq en */ | |
77 | #define FIER_TXIE 0x01 /* tx fifo trig irq en */ | |
78 | ||
79 | #define FIFO_SIZE 16 | |
80 | ||
81 | struct cami2c { | |
82 | void __iomem *iobase; | |
83 | struct i2c_adapter adap; | |
84 | ||
85 | /* message processing */ | |
86 | struct i2c_msg *msg; | |
87 | #define IDF_SEND 1 | |
88 | #define IDF_RECV 2 | |
89 | #define IDF_STOP 4 | |
90 | int flags; | |
91 | ||
92 | #define IDS_DONE 1 | |
93 | #define IDS_ARBLOST 2 | |
94 | #define IDS_NACK 4 | |
95 | int status; | |
96 | struct completion xfer_done; | |
97 | ||
98 | int irq; | |
99 | struct resource *ioarea; | |
100 | }; | |
101 | ||
102 | static inline void OUT32(struct cami2c *cam, int reg, unsigned long val) | |
103 | { | |
8df39901 | 104 | __raw_writel(val, (unsigned long)cam->iobase + reg); |
a26c20b1 ML |
105 | } |
106 | ||
107 | static inline unsigned long IN32(struct cami2c *cam, int reg) | |
108 | { | |
8df39901 | 109 | return __raw_readl((unsigned long)cam->iobase + reg); |
a26c20b1 ML |
110 | } |
111 | ||
112 | static irqreturn_t sh7760_i2c_irq(int irq, void *ptr) | |
113 | { | |
114 | struct cami2c *id = ptr; | |
115 | struct i2c_msg *msg = id->msg; | |
116 | char *data = msg->buf; | |
117 | unsigned long msr, fsr, fier, len; | |
118 | ||
119 | msr = IN32(id, I2CMSR); | |
120 | fsr = IN32(id, I2CFSR); | |
121 | ||
122 | /* arbitration lost */ | |
123 | if (msr & MSR_MAL) { | |
124 | OUT32(id, I2CMCR, 0); | |
125 | OUT32(id, I2CSCR, 0); | |
126 | OUT32(id, I2CSAR, 0); | |
127 | id->status |= IDS_DONE | IDS_ARBLOST; | |
128 | goto out; | |
129 | } | |
130 | ||
131 | if (msr & MSR_MNR) { | |
132 | /* NACK handling is very screwed up. After receiving a | |
133 | * NAK IRQ one has to wait a bit before writing to any | |
134 | * registers, or the ctl will lock up. After that delay | |
135 | * do a normal i2c stop. Then wait at least 1 ms before | |
136 | * attempting another transfer or ctl will stop working | |
137 | */ | |
138 | udelay(100); /* wait or risk ctl hang */ | |
139 | OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); | |
140 | OUT32(id, I2CMCR, MCR_MIE | MCR_FSB); | |
141 | OUT32(id, I2CFIER, 0); | |
142 | OUT32(id, I2CMIER, MIE_MSTE); | |
143 | OUT32(id, I2CSCR, 0); | |
144 | OUT32(id, I2CSAR, 0); | |
145 | id->status |= IDS_NACK; | |
146 | msr &= ~MSR_MAT; | |
147 | fsr = 0; | |
148 | /* In some cases the MST bit is also set. */ | |
149 | } | |
150 | ||
151 | /* i2c-stop was sent */ | |
152 | if (msr & MSR_MST) { | |
153 | id->status |= IDS_DONE; | |
154 | goto out; | |
155 | } | |
156 | ||
157 | /* i2c slave addr was sent; set to "normal" operation */ | |
158 | if (msr & MSR_MAT) | |
159 | OUT32(id, I2CMCR, MCR_MIE); | |
160 | ||
161 | fier = IN32(id, I2CFIER); | |
162 | ||
163 | if (fsr & FSR_RDF) { | |
164 | len = IN32(id, I2CRFDR); | |
165 | if (msg->len <= len) { | |
166 | if (id->flags & IDF_STOP) { | |
167 | OUT32(id, I2CMCR, MCR_MIE | MCR_FSB); | |
168 | OUT32(id, I2CFIER, 0); | |
169 | /* manual says: wait >= 0.5 SCL times */ | |
170 | udelay(5); | |
171 | /* next int should be MST */ | |
172 | } else { | |
173 | id->status |= IDS_DONE; | |
174 | /* keep the RDF bit: ctrl holds SCL low | |
175 | * until the setup for the next i2c_msg | |
176 | * clears this bit. | |
177 | */ | |
178 | fsr &= ~FSR_RDF; | |
179 | } | |
180 | } | |
181 | while (msg->len && len) { | |
182 | *data++ = IN32(id, I2CRXTX); | |
183 | msg->len--; | |
184 | len--; | |
185 | } | |
186 | ||
187 | if (msg->len) { | |
188 | len = (msg->len >= FIFO_SIZE) ? FIFO_SIZE - 1 | |
189 | : msg->len - 1; | |
190 | ||
191 | OUT32(id, I2CFCR, FCR_TFRST | ((len & 0xf) << 4)); | |
192 | } | |
193 | ||
194 | } else if (id->flags & IDF_SEND) { | |
195 | if ((fsr & FSR_TEND) && (msg->len < 1)) { | |
196 | if (id->flags & IDF_STOP) { | |
197 | OUT32(id, I2CMCR, MCR_MIE | MCR_FSB); | |
198 | } else { | |
199 | id->status |= IDS_DONE; | |
200 | /* keep the TEND bit: ctl holds SCL low | |
201 | * until the setup for the next i2c_msg | |
202 | * clears this bit. | |
203 | */ | |
204 | fsr &= ~FSR_TEND; | |
205 | } | |
206 | } | |
207 | if (fsr & FSR_TDFE) { | |
208 | while (msg->len && (IN32(id, I2CTFDR) < FIFO_SIZE)) { | |
209 | OUT32(id, I2CRXTX, *data++); | |
210 | msg->len--; | |
211 | } | |
212 | ||
213 | if (msg->len < 1) { | |
214 | fier &= ~FIER_TXIE; | |
215 | OUT32(id, I2CFIER, fier); | |
216 | } else { | |
217 | len = (msg->len >= FIFO_SIZE) ? 2 : 0; | |
218 | OUT32(id, I2CFCR, | |
219 | FCR_RFRST | ((len & 3) << 2)); | |
220 | } | |
221 | } | |
222 | } | |
223 | out: | |
224 | if (id->status & IDS_DONE) { | |
225 | OUT32(id, I2CMIER, 0); | |
226 | OUT32(id, I2CFIER, 0); | |
227 | id->msg = NULL; | |
228 | complete(&id->xfer_done); | |
229 | } | |
230 | /* clear status flags and ctrl resumes work */ | |
231 | OUT32(id, I2CMSR, ~msr); | |
232 | OUT32(id, I2CFSR, ~fsr); | |
233 | OUT32(id, I2CSSR, 0); | |
234 | ||
235 | return IRQ_HANDLED; | |
236 | } | |
237 | ||
238 | ||
239 | /* prepare and start a master receive operation */ | |
240 | static void sh7760_i2c_mrecv(struct cami2c *id) | |
241 | { | |
242 | int len; | |
243 | ||
244 | id->flags |= IDF_RECV; | |
245 | ||
246 | /* set the slave addr reg; otherwise rcv wont work! */ | |
247 | OUT32(id, I2CSAR, 0xfe); | |
248 | OUT32(id, I2CMAR, (id->msg->addr << 1) | 1); | |
249 | ||
250 | /* adjust rx fifo trigger */ | |
251 | if (id->msg->len >= FIFO_SIZE) | |
252 | len = FIFO_SIZE - 1; /* trigger at fifo full */ | |
253 | else | |
254 | len = id->msg->len - 1; /* trigger before all received */ | |
255 | ||
256 | OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); | |
257 | OUT32(id, I2CFCR, FCR_TFRST | ((len & 0xF) << 4)); | |
258 | ||
259 | OUT32(id, I2CMSR, 0); | |
260 | OUT32(id, I2CMCR, MCR_MIE | MCR_ESG); | |
261 | OUT32(id, I2CMIER, MIE_MNRE | MIE_MALE | MIE_MSTE | MIE_MATE); | |
262 | OUT32(id, I2CFIER, FIER_RXIE); | |
263 | } | |
264 | ||
265 | /* prepare and start a master send operation */ | |
266 | static void sh7760_i2c_msend(struct cami2c *id) | |
267 | { | |
268 | int len; | |
269 | ||
270 | id->flags |= IDF_SEND; | |
271 | ||
272 | /* set the slave addr reg; otherwise xmit wont work! */ | |
273 | OUT32(id, I2CSAR, 0xfe); | |
274 | OUT32(id, I2CMAR, (id->msg->addr << 1) | 0); | |
275 | ||
276 | /* adjust tx fifo trigger */ | |
277 | if (id->msg->len >= FIFO_SIZE) | |
278 | len = 2; /* trig: 2 bytes left in TX fifo */ | |
279 | else | |
280 | len = 0; /* trig: 8 bytes left in TX fifo */ | |
281 | ||
282 | OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); | |
283 | OUT32(id, I2CFCR, FCR_RFRST | ((len & 3) << 2)); | |
284 | ||
285 | while (id->msg->len && IN32(id, I2CTFDR) < FIFO_SIZE) { | |
286 | OUT32(id, I2CRXTX, *(id->msg->buf)); | |
287 | (id->msg->len)--; | |
288 | (id->msg->buf)++; | |
289 | } | |
290 | ||
291 | OUT32(id, I2CMSR, 0); | |
292 | OUT32(id, I2CMCR, MCR_MIE | MCR_ESG); | |
293 | OUT32(id, I2CFSR, 0); | |
294 | OUT32(id, I2CMIER, MIE_MNRE | MIE_MALE | MIE_MSTE | MIE_MATE); | |
295 | OUT32(id, I2CFIER, FIER_TEIE | (id->msg->len ? FIER_TXIE : 0)); | |
296 | } | |
297 | ||
298 | static inline int sh7760_i2c_busy_check(struct cami2c *id) | |
299 | { | |
300 | return (IN32(id, I2CMCR) & MCR_FSDA); | |
301 | } | |
302 | ||
303 | static int sh7760_i2c_master_xfer(struct i2c_adapter *adap, | |
304 | struct i2c_msg *msgs, | |
305 | int num) | |
306 | { | |
307 | struct cami2c *id = adap->algo_data; | |
308 | int i, retr; | |
309 | ||
310 | if (sh7760_i2c_busy_check(id)) { | |
311 | dev_err(&adap->dev, "sh7760-i2c%d: bus busy!\n", adap->nr); | |
312 | return -EBUSY; | |
313 | } | |
314 | ||
315 | i = 0; | |
316 | while (i < num) { | |
317 | retr = adap->retries; | |
318 | retry: | |
319 | id->flags = ((i == (num-1)) ? IDF_STOP : 0); | |
320 | id->status = 0; | |
321 | id->msg = msgs; | |
322 | init_completion(&id->xfer_done); | |
323 | ||
324 | if (msgs->flags & I2C_M_RD) | |
325 | sh7760_i2c_mrecv(id); | |
326 | else | |
327 | sh7760_i2c_msend(id); | |
328 | ||
329 | wait_for_completion(&id->xfer_done); | |
330 | ||
331 | if (id->status == 0) { | |
332 | num = -EIO; | |
333 | break; | |
334 | } | |
335 | ||
336 | if (id->status & IDS_NACK) { | |
337 | /* wait a bit or i2c module stops working */ | |
338 | mdelay(1); | |
339 | num = -EREMOTEIO; | |
340 | break; | |
341 | } | |
342 | ||
343 | if (id->status & IDS_ARBLOST) { | |
344 | if (retr--) { | |
345 | mdelay(2); | |
346 | goto retry; | |
347 | } | |
348 | num = -EREMOTEIO; | |
349 | break; | |
350 | } | |
351 | ||
352 | msgs++; | |
353 | i++; | |
354 | } | |
355 | ||
356 | id->msg = NULL; | |
357 | id->flags = 0; | |
358 | id->status = 0; | |
359 | ||
360 | OUT32(id, I2CMCR, 0); | |
361 | OUT32(id, I2CMSR, 0); | |
362 | OUT32(id, I2CMIER, 0); | |
363 | OUT32(id, I2CFIER, 0); | |
364 | ||
365 | /* reset slave module registers too: master mode enables slave | |
366 | * module for receive ops (ack, data). Without this reset, | |
367 | * eternal bus activity might be reported after NACK / ARBLOST. | |
368 | */ | |
369 | OUT32(id, I2CSCR, 0); | |
370 | OUT32(id, I2CSAR, 0); | |
371 | OUT32(id, I2CSSR, 0); | |
372 | ||
373 | return num; | |
374 | } | |
375 | ||
376 | static u32 sh7760_i2c_func(struct i2c_adapter *adap) | |
377 | { | |
378 | return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); | |
379 | } | |
380 | ||
381 | static const struct i2c_algorithm sh7760_i2c_algo = { | |
382 | .master_xfer = sh7760_i2c_master_xfer, | |
383 | .functionality = sh7760_i2c_func, | |
384 | }; | |
385 | ||
386 | /* calculate CCR register setting for a desired scl clock. SCL clock is | |
387 | * derived from I2C module clock (iclk) which in turn is derived from | |
388 | * peripheral module clock (mclk, usually around 33MHz): | |
389 | * iclk = mclk/(CDF + 1). iclk must be < 20MHz. | |
390 | * scl = iclk/(SCGD*8 + 20). | |
391 | */ | |
0b255e92 | 392 | static int calc_CCR(unsigned long scl_hz) |
a26c20b1 ML |
393 | { |
394 | struct clk *mclk; | |
395 | unsigned long mck, m1, dff, odff, iclk; | |
396 | signed char cdf, cdfm; | |
397 | int scgd, scgdm, scgds; | |
398 | ||
af777ce4 | 399 | mclk = clk_get(NULL, "peripheral_clk"); |
a26c20b1 ML |
400 | if (IS_ERR(mclk)) { |
401 | return PTR_ERR(mclk); | |
402 | } else { | |
403 | mck = mclk->rate; | |
404 | clk_put(mclk); | |
405 | } | |
406 | ||
407 | odff = scl_hz; | |
408 | scgdm = cdfm = m1 = 0; | |
409 | for (cdf = 3; cdf >= 0; cdf--) { | |
410 | iclk = mck / (1 + cdf); | |
411 | if (iclk >= 20000000) | |
412 | continue; | |
413 | scgds = ((iclk / scl_hz) - 20) >> 3; | |
414 | for (scgd = scgds; (scgd < 63) && scgd <= scgds + 1; scgd++) { | |
415 | m1 = iclk / (20 + (scgd << 3)); | |
416 | dff = abs(scl_hz - m1); | |
417 | if (dff < odff) { | |
418 | odff = dff; | |
419 | cdfm = cdf; | |
420 | scgdm = scgd; | |
421 | } | |
422 | } | |
423 | } | |
424 | /* fail if more than 25% off of requested SCL */ | |
425 | if (odff > (scl_hz >> 2)) | |
426 | return -EINVAL; | |
427 | ||
428 | /* create a CCR register value */ | |
429 | return ((scgdm << 2) | cdfm); | |
430 | } | |
431 | ||
0b255e92 | 432 | static int sh7760_i2c_probe(struct platform_device *pdev) |
a26c20b1 ML |
433 | { |
434 | struct sh7760_i2c_platdata *pd; | |
435 | struct resource *res; | |
436 | struct cami2c *id; | |
437 | int ret; | |
438 | ||
6d4028c6 | 439 | pd = dev_get_platdata(&pdev->dev); |
a26c20b1 ML |
440 | if (!pd) { |
441 | dev_err(&pdev->dev, "no platform_data!\n"); | |
442 | ret = -ENODEV; | |
443 | goto out0; | |
444 | } | |
445 | ||
446 | id = kzalloc(sizeof(struct cami2c), GFP_KERNEL); | |
447 | if (!id) { | |
448 | dev_err(&pdev->dev, "no mem for private data\n"); | |
449 | ret = -ENOMEM; | |
450 | goto out0; | |
451 | } | |
452 | ||
453 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
454 | if (!res) { | |
455 | dev_err(&pdev->dev, "no mmio resources\n"); | |
456 | ret = -ENODEV; | |
457 | goto out1; | |
458 | } | |
459 | ||
460 | id->ioarea = request_mem_region(res->start, REGSIZE, pdev->name); | |
461 | if (!id->ioarea) { | |
462 | dev_err(&pdev->dev, "mmio already reserved\n"); | |
463 | ret = -EBUSY; | |
464 | goto out1; | |
465 | } | |
466 | ||
467 | id->iobase = ioremap(res->start, REGSIZE); | |
468 | if (!id->iobase) { | |
469 | dev_err(&pdev->dev, "cannot ioremap\n"); | |
470 | ret = -ENODEV; | |
471 | goto out2; | |
472 | } | |
473 | ||
474 | id->irq = platform_get_irq(pdev, 0); | |
475 | ||
476 | id->adap.nr = pdev->id; | |
477 | id->adap.algo = &sh7760_i2c_algo; | |
e1995f65 | 478 | id->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; |
a26c20b1 ML |
479 | id->adap.retries = 3; |
480 | id->adap.algo_data = id; | |
481 | id->adap.dev.parent = &pdev->dev; | |
482 | snprintf(id->adap.name, sizeof(id->adap.name), | |
483 | "SH7760 I2C at %08lx", (unsigned long)res->start); | |
484 | ||
485 | OUT32(id, I2CMCR, 0); | |
486 | OUT32(id, I2CMSR, 0); | |
487 | OUT32(id, I2CMIER, 0); | |
488 | OUT32(id, I2CMAR, 0); | |
489 | OUT32(id, I2CSIER, 0); | |
490 | OUT32(id, I2CSAR, 0); | |
491 | OUT32(id, I2CSCR, 0); | |
492 | OUT32(id, I2CSSR, 0); | |
493 | OUT32(id, I2CFIER, 0); | |
494 | OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); | |
495 | OUT32(id, I2CFSR, 0); | |
496 | ||
497 | ret = calc_CCR(pd->speed_khz * 1000); | |
498 | if (ret < 0) { | |
499 | dev_err(&pdev->dev, "invalid SCL clock: %dkHz\n", | |
500 | pd->speed_khz); | |
501 | goto out3; | |
502 | } | |
503 | OUT32(id, I2CCCR, ret); | |
504 | ||
4311051c | 505 | if (request_irq(id->irq, sh7760_i2c_irq, 0, |
a26c20b1 ML |
506 | SH7760_I2C_DEVNAME, id)) { |
507 | dev_err(&pdev->dev, "cannot get irq %d\n", id->irq); | |
508 | ret = -EBUSY; | |
509 | goto out3; | |
510 | } | |
511 | ||
512 | ret = i2c_add_numbered_adapter(&id->adap); | |
513 | if (ret < 0) { | |
514 | dev_err(&pdev->dev, "reg adap failed: %d\n", ret); | |
515 | goto out4; | |
516 | } | |
517 | ||
518 | platform_set_drvdata(pdev, id); | |
519 | ||
520 | dev_info(&pdev->dev, "%d kHz mmio %08x irq %d\n", | |
521 | pd->speed_khz, res->start, id->irq); | |
522 | ||
523 | return 0; | |
524 | ||
525 | out4: | |
526 | free_irq(id->irq, id); | |
527 | out3: | |
528 | iounmap(id->iobase); | |
529 | out2: | |
530 | release_resource(id->ioarea); | |
531 | kfree(id->ioarea); | |
532 | out1: | |
533 | kfree(id); | |
534 | out0: | |
535 | return ret; | |
536 | } | |
537 | ||
0b255e92 | 538 | static int sh7760_i2c_remove(struct platform_device *pdev) |
a26c20b1 ML |
539 | { |
540 | struct cami2c *id = platform_get_drvdata(pdev); | |
541 | ||
542 | i2c_del_adapter(&id->adap); | |
543 | free_irq(id->irq, id); | |
544 | iounmap(id->iobase); | |
545 | release_resource(id->ioarea); | |
546 | kfree(id->ioarea); | |
547 | kfree(id); | |
a26c20b1 ML |
548 | |
549 | return 0; | |
550 | } | |
551 | ||
552 | static struct platform_driver sh7760_i2c_drv = { | |
553 | .driver = { | |
554 | .name = SH7760_I2C_DEVNAME, | |
a26c20b1 ML |
555 | }, |
556 | .probe = sh7760_i2c_probe, | |
0b255e92 | 557 | .remove = sh7760_i2c_remove, |
a26c20b1 ML |
558 | }; |
559 | ||
a3664b51 | 560 | module_platform_driver(sh7760_i2c_drv); |
a26c20b1 ML |
561 | |
562 | MODULE_LICENSE("GPL"); | |
563 | MODULE_DESCRIPTION("SH7760 I2C bus driver"); | |
564 | MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); |