diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-au1550.c')
-rw-r--r-- | drivers/i2c/busses/i2c-au1550.c | 166 |
1 files changed, 102 insertions, 64 deletions
diff --git a/drivers/i2c/busses/i2c-au1550.c b/drivers/i2c/busses/i2c-au1550.c index 7d51a43b38d3..1953b26da56a 100644 --- a/drivers/i2c/busses/i2c-au1550.c +++ b/drivers/i2c/busses/i2c-au1550.c | |||
@@ -30,14 +30,22 @@ | |||
30 | #include <linux/delay.h> | 30 | #include <linux/delay.h> |
31 | #include <linux/kernel.h> | 31 | #include <linux/kernel.h> |
32 | #include <linux/module.h> | 32 | #include <linux/module.h> |
33 | #include <linux/platform_device.h> | ||
33 | #include <linux/init.h> | 34 | #include <linux/init.h> |
34 | #include <linux/errno.h> | 35 | #include <linux/errno.h> |
35 | #include <linux/i2c.h> | 36 | #include <linux/i2c.h> |
37 | #include <linux/slab.h> | ||
36 | 38 | ||
37 | #include <asm/mach-au1x00/au1xxx.h> | 39 | #include <asm/mach-au1x00/au1xxx.h> |
38 | #include <asm/mach-au1x00/au1xxx_psc.h> | 40 | #include <asm/mach-au1x00/au1xxx_psc.h> |
39 | 41 | ||
40 | #include "i2c-au1550.h" | 42 | struct i2c_au1550_data { |
43 | u32 psc_base; | ||
44 | int xfer_timeout; | ||
45 | int ack_timeout; | ||
46 | struct i2c_adapter adap; | ||
47 | struct resource *ioarea; | ||
48 | }; | ||
41 | 49 | ||
42 | static int | 50 | static int |
43 | wait_xfer_done(struct i2c_au1550_data *adap) | 51 | wait_xfer_done(struct i2c_au1550_data *adap) |
@@ -299,18 +307,48 @@ static const struct i2c_algorithm au1550_algo = { | |||
299 | * Prior to calling us, the 50MHz clock frequency and routing | 307 | * Prior to calling us, the 50MHz clock frequency and routing |
300 | * must have been set up for the PSC indicated by the adapter. | 308 | * must have been set up for the PSC indicated by the adapter. |
301 | */ | 309 | */ |
302 | int | 310 | static int __devinit |
303 | i2c_au1550_add_bus(struct i2c_adapter *i2c_adap) | 311 | i2c_au1550_probe(struct platform_device *pdev) |
304 | { | 312 | { |
305 | struct i2c_au1550_data *adap = i2c_adap->algo_data; | 313 | struct i2c_au1550_data *priv; |
306 | volatile psc_smb_t *sp; | 314 | volatile psc_smb_t *sp; |
307 | u32 stat; | 315 | struct resource *r; |
316 | u32 stat; | ||
317 | int ret; | ||
318 | |||
319 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
320 | if (!r) { | ||
321 | ret = -ENODEV; | ||
322 | goto out; | ||
323 | } | ||
308 | 324 | ||
309 | i2c_adap->algo = &au1550_algo; | 325 | priv = kzalloc(sizeof(struct i2c_au1550_data), GFP_KERNEL); |
326 | if (!priv) { | ||
327 | ret = -ENOMEM; | ||
328 | goto out; | ||
329 | } | ||
330 | |||
331 | priv->ioarea = request_mem_region(r->start, r->end - r->start + 1, | ||
332 | pdev->name); | ||
333 | if (!priv->ioarea) { | ||
334 | ret = -EBUSY; | ||
335 | goto out_mem; | ||
336 | } | ||
337 | |||
338 | priv->psc_base = r->start; | ||
339 | priv->xfer_timeout = 200; | ||
340 | priv->ack_timeout = 200; | ||
341 | |||
342 | priv->adap.id = I2C_HW_AU1550_PSC; | ||
343 | priv->adap.nr = pdev->id; | ||
344 | priv->adap.algo = &au1550_algo; | ||
345 | priv->adap.algo_data = priv; | ||
346 | priv->adap.dev.parent = &pdev->dev; | ||
347 | strlcpy(priv->adap.name, "Au1xxx PSC I2C", sizeof(priv->adap.name)); | ||
310 | 348 | ||
311 | /* Now, set up the PSC for SMBus PIO mode. | 349 | /* Now, set up the PSC for SMBus PIO mode. |
312 | */ | 350 | */ |
313 | sp = (volatile psc_smb_t *)(adap->psc_base); | 351 | sp = (volatile psc_smb_t *)priv->psc_base; |
314 | sp->psc_ctrl = PSC_CTRL_DISABLE; | 352 | sp->psc_ctrl = PSC_CTRL_DISABLE; |
315 | au_sync(); | 353 | au_sync(); |
316 | sp->psc_sel = PSC_SEL_PS_SMBUSMODE; | 354 | sp->psc_sel = PSC_SEL_PS_SMBUSMODE; |
@@ -348,87 +386,87 @@ i2c_au1550_add_bus(struct i2c_adapter *i2c_adap) | |||
348 | au_sync(); | 386 | au_sync(); |
349 | } while ((stat & PSC_SMBSTAT_DR) == 0); | 387 | } while ((stat & PSC_SMBSTAT_DR) == 0); |
350 | 388 | ||
351 | return i2c_add_adapter(i2c_adap); | 389 | ret = i2c_add_numbered_adapter(&priv->adap); |
352 | } | 390 | if (ret == 0) { |
391 | platform_set_drvdata(pdev, priv); | ||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | /* disable the PSC */ | ||
396 | sp->psc_smbcfg = 0; | ||
397 | sp->psc_ctrl = PSC_CTRL_DISABLE; | ||
398 | au_sync(); | ||
353 | 399 | ||
400 | release_resource(priv->ioarea); | ||
401 | kfree(priv->ioarea); | ||
402 | out_mem: | ||
403 | kfree(priv); | ||
404 | out: | ||
405 | return ret; | ||
406 | } | ||
354 | 407 | ||
355 | int | 408 | static int __devexit |
356 | i2c_au1550_del_bus(struct i2c_adapter *adap) | 409 | i2c_au1550_remove(struct platform_device *pdev) |
357 | { | 410 | { |
358 | return i2c_del_adapter(adap); | 411 | struct i2c_au1550_data *priv = platform_get_drvdata(pdev); |
412 | volatile psc_smb_t *sp = (volatile psc_smb_t *)priv->psc_base; | ||
413 | |||
414 | platform_set_drvdata(pdev, NULL); | ||
415 | i2c_del_adapter(&priv->adap); | ||
416 | sp->psc_smbcfg = 0; | ||
417 | sp->psc_ctrl = PSC_CTRL_DISABLE; | ||
418 | au_sync(); | ||
419 | release_resource(priv->ioarea); | ||
420 | kfree(priv->ioarea); | ||
421 | kfree(priv); | ||
422 | return 0; | ||
359 | } | 423 | } |
360 | 424 | ||
361 | static int | 425 | static int |
362 | pb1550_reg(struct i2c_client *client) | 426 | i2c_au1550_suspend(struct platform_device *pdev, pm_message_t state) |
363 | { | 427 | { |
428 | struct i2c_au1550_data *priv = platform_get_drvdata(pdev); | ||
429 | volatile psc_smb_t *sp = (volatile psc_smb_t *)priv->psc_base; | ||
430 | |||
431 | sp->psc_ctrl = PSC_CTRL_SUSPEND; | ||
432 | au_sync(); | ||
364 | return 0; | 433 | return 0; |
365 | } | 434 | } |
366 | 435 | ||
367 | static int | 436 | static int |
368 | pb1550_unreg(struct i2c_client *client) | 437 | i2c_au1550_resume(struct platform_device *pdev) |
369 | { | 438 | { |
439 | struct i2c_au1550_data *priv = platform_get_drvdata(pdev); | ||
440 | volatile psc_smb_t *sp = (volatile psc_smb_t *)priv->psc_base; | ||
441 | |||
442 | sp->psc_ctrl = PSC_CTRL_ENABLE; | ||
443 | au_sync(); | ||
444 | while (!(sp->psc_smbstat & PSC_SMBSTAT_SR)) | ||
445 | au_sync(); | ||
370 | return 0; | 446 | return 0; |
371 | } | 447 | } |
372 | 448 | ||
373 | static struct i2c_au1550_data pb1550_i2c_info = { | 449 | static struct platform_driver au1xpsc_smbus_driver = { |
374 | SMBUS_PSC_BASE, 200, 200 | 450 | .driver = { |
375 | }; | 451 | .name = "au1xpsc_smbus", |
376 | 452 | .owner = THIS_MODULE, | |
377 | static struct i2c_adapter pb1550_board_adapter = { | 453 | }, |
378 | name: "pb1550 adapter", | 454 | .probe = i2c_au1550_probe, |
379 | id: I2C_HW_AU1550_PSC, | 455 | .remove = __devexit_p(i2c_au1550_remove), |
380 | algo: NULL, | 456 | .suspend = i2c_au1550_suspend, |
381 | algo_data: &pb1550_i2c_info, | 457 | .resume = i2c_au1550_resume, |
382 | client_register: pb1550_reg, | ||
383 | client_unregister: pb1550_unreg, | ||
384 | }; | 458 | }; |
385 | 459 | ||
386 | /* BIG hack to support the control interface on the Wolfson WM8731 | ||
387 | * audio codec on the Pb1550 board. We get an address and two data | ||
388 | * bytes to write, create an i2c message, and send it across the | ||
389 | * i2c transfer function. We do this here because we have access to | ||
390 | * the i2c adapter structure. | ||
391 | */ | ||
392 | static struct i2c_msg wm_i2c_msg; /* We don't want this stuff on the stack */ | ||
393 | static u8 i2cbuf[2]; | ||
394 | |||
395 | int | ||
396 | pb1550_wm_codec_write(u8 addr, u8 reg, u8 val) | ||
397 | { | ||
398 | wm_i2c_msg.addr = addr; | ||
399 | wm_i2c_msg.flags = 0; | ||
400 | wm_i2c_msg.buf = i2cbuf; | ||
401 | wm_i2c_msg.len = 2; | ||
402 | i2cbuf[0] = reg; | ||
403 | i2cbuf[1] = val; | ||
404 | |||
405 | return pb1550_board_adapter.algo->master_xfer(&pb1550_board_adapter, &wm_i2c_msg, 1); | ||
406 | } | ||
407 | |||
408 | static int __init | 460 | static int __init |
409 | i2c_au1550_init(void) | 461 | i2c_au1550_init(void) |
410 | { | 462 | { |
411 | printk(KERN_INFO "Au1550 I2C: "); | 463 | return platform_driver_register(&au1xpsc_smbus_driver); |
412 | |||
413 | /* This is where we would set up a 50MHz clock source | ||
414 | * and routing. On the Pb1550, the SMBus is PSC2, which | ||
415 | * uses a shared clock with USB. This has been already | ||
416 | * configured by Yamon as a 48MHz clock, close enough | ||
417 | * for our work. | ||
418 | */ | ||
419 | if (i2c_au1550_add_bus(&pb1550_board_adapter) < 0) { | ||
420 | printk("failed to initialize.\n"); | ||
421 | return -ENODEV; | ||
422 | } | ||
423 | |||
424 | printk("initialized.\n"); | ||
425 | return 0; | ||
426 | } | 464 | } |
427 | 465 | ||
428 | static void __exit | 466 | static void __exit |
429 | i2c_au1550_exit(void) | 467 | i2c_au1550_exit(void) |
430 | { | 468 | { |
431 | i2c_au1550_del_bus(&pb1550_board_adapter); | 469 | platform_driver_unregister(&au1xpsc_smbus_driver); |
432 | } | 470 | } |
433 | 471 | ||
434 | MODULE_AUTHOR("Dan Malek, Embedded Edge, LLC."); | 472 | MODULE_AUTHOR("Dan Malek, Embedded Edge, LLC."); |