/* * Copyright (C) 2009 ST-Ericsson * * Author: Srinidhi KASAGAR * * 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 #include #include #include #include #include #include /* 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