123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- /*
- * Copyright (C) 2009 ST-Ericsson
- *
- * Author: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com>
- *
- * This program is free software; you can redistribute it
- * and/or modify it under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation.
- *
- * AB4500 is a companion power management chip used with U8500.
- * On this platform, this is interfaced with SSP0 controller
- * which is a ARM primecell pl022.
- *
- * At the moment the module just exports read/write features.
- * Interrupt management to be added - TODO.
- */
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/platform_device.h>
- #include <linux/spi/spi.h>
- #include <linux/mfd/ab4500.h>
- /* just required if probe fails, we need to
- * unregister the device
- */
- static struct spi_driver ab4500_driver;
- /*
- * This funtion writes to any AB4500 registers using
- * SPI protocol & before it writes it packs the data
- * in the below 24 bit frame format
- *
- * *|------------------------------------|
- * *| 23|22...18|17.......10|9|8|7......0|
- * *| r/w bank adr data |
- * * ------------------------------------
- *
- * This function shouldn't be called from interrupt
- * context
- */
- int ab4500_write(struct ab4500 *ab4500, unsigned char block,
- unsigned long addr, unsigned char data)
- {
- struct spi_transfer xfer;
- struct spi_message msg;
- int err;
- unsigned long spi_data =
- block << 18 | addr << 10 | data;
- mutex_lock(&ab4500->lock);
- ab4500->tx_buf[0] = spi_data;
- ab4500->rx_buf[0] = 0;
- xfer.tx_buf = ab4500->tx_buf;
- xfer.rx_buf = NULL;
- xfer.len = sizeof(unsigned long);
- spi_message_init(&msg);
- spi_message_add_tail(&xfer, &msg);
- err = spi_sync(ab4500->spi, &msg);
- mutex_unlock(&ab4500->lock);
- return err;
- }
- EXPORT_SYMBOL(ab4500_write);
- int ab4500_read(struct ab4500 *ab4500, unsigned char block,
- unsigned long addr)
- {
- struct spi_transfer xfer;
- struct spi_message msg;
- unsigned long spi_data =
- 1 << 23 | block << 18 | addr << 10;
- mutex_lock(&ab4500->lock);
- ab4500->tx_buf[0] = spi_data;
- ab4500->rx_buf[0] = 0;
- xfer.tx_buf = ab4500->tx_buf;
- xfer.rx_buf = ab4500->rx_buf;
- xfer.len = sizeof(unsigned long);
- spi_message_init(&msg);
- spi_message_add_tail(&xfer, &msg);
- spi_sync(ab4500->spi, &msg);
- mutex_unlock(&ab4500->lock);
- return ab4500->rx_buf[0];
- }
- EXPORT_SYMBOL(ab4500_read);
- /* ref: ab3100 core */
- #define AB4500_DEVICE(devname, devid) \
- static struct platform_device ab4500_##devname##_device = { \
- .name = devid, \
- .id = -1, \
- }
- /* list of childern devices of ab4500 - all are
- * not populated here - TODO
- */
- AB4500_DEVICE(charger, "ab4500-charger");
- AB4500_DEVICE(audio, "ab4500-audio");
- AB4500_DEVICE(usb, "ab4500-usb");
- AB4500_DEVICE(tvout, "ab4500-tvout");
- AB4500_DEVICE(sim, "ab4500-sim");
- AB4500_DEVICE(gpadc, "ab4500-gpadc");
- AB4500_DEVICE(clkmgt, "ab4500-clkmgt");
- AB4500_DEVICE(misc, "ab4500-misc");
- static struct platform_device *ab4500_platform_devs[] = {
- &ab4500_charger_device,
- &ab4500_audio_device,
- &ab4500_usb_device,
- &ab4500_tvout_device,
- &ab4500_sim_device,
- &ab4500_gpadc_device,
- &ab4500_clkmgt_device,
- &ab4500_misc_device,
- };
- static int __init ab4500_probe(struct spi_device *spi)
- {
- struct ab4500 *ab4500;
- unsigned char revision;
- int err = 0;
- int i;
- ab4500 = kzalloc(sizeof *ab4500, GFP_KERNEL);
- if (!ab4500) {
- dev_err(&spi->dev, "could not allocate AB4500\n");
- err = -ENOMEM;
- goto not_detect;
- }
- ab4500->spi = spi;
- spi_set_drvdata(spi, ab4500);
- mutex_init(&ab4500->lock);
- /* read the revision register */
- revision = ab4500_read(ab4500, AB4500_MISC, AB4500_REV_REG);
- /* revision id 0x0 is for early drop, 0x10 is for cut1.0 */
- if (revision == 0x0 || revision == 0x10)
- dev_info(&spi->dev, "Detected chip: %s, revision = %x\n",
- ab4500_driver.driver.name, revision);
- else {
- dev_err(&spi->dev, "unknown chip: 0x%x\n", revision);
- goto not_detect;
- }
- for (i = 0; i < ARRAY_SIZE(ab4500_platform_devs); i++) {
- ab4500_platform_devs[i]->dev.parent =
- &spi->dev;
- platform_set_drvdata(ab4500_platform_devs[i], ab4500);
- }
- /* register the ab4500 platform devices */
- platform_add_devices(ab4500_platform_devs,
- ARRAY_SIZE(ab4500_platform_devs));
- return err;
- not_detect:
- spi_unregister_driver(&ab4500_driver);
- kfree(ab4500);
- return err;
- }
- static int __devexit ab4500_remove(struct spi_device *spi)
- {
- struct ab4500 *ab4500 =
- spi_get_drvdata(spi);
- kfree(ab4500);
- return 0;
- }
- static struct spi_driver ab4500_driver = {
- .driver = {
- .name = "ab4500",
- .owner = THIS_MODULE,
- },
- .probe = ab4500_probe,
- .remove = __devexit_p(ab4500_remove)
- };
- static int __devinit ab4500_init(void)
- {
- return spi_register_driver(&ab4500_driver);
- }
- static void __exit ab4500_exit(void)
- {
- spi_unregister_driver(&ab4500_driver);
- }
- subsys_initcall(ab4500_init);
- module_exit(ab4500_exit);
- MODULE_AUTHOR("Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com");
- MODULE_DESCRIPTION("AB4500 core driver");
- MODULE_LICENSE("GPL");
|