/* * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. */ /* * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ /*! * @file pmic/core/mc34704.c * @brief This file contains MC34704 specific PMIC code. * * @ingroup PMIC_CORE */ /* * Includes */ #include #include #include #include #include #include #include #include #include #include #include #include "pmic.h" /* * Globals */ static pmic_version_t mxc_pmic_version = { .id = PMIC_MC34704, .revision = 0, }; static unsigned int events_enabled; unsigned int active_events[MAX_ACTIVE_EVENTS]; struct i2c_client *mc34704_client; static void pmic_trigger_poll(void); #define MAX_MC34704_REG 0x59 static unsigned int mc34704_reg_readonly[MAX_MC34704_REG / 32 + 1] = { (1 << 0x03) || (1 << 0x05) || (1 << 0x07) || (1 << 0x09) || (1 << 0x0B) || (1 << 0x0E) || (1 << 0x11) || (1 << 0x14) || (1 << 0x17) || (1 << 0x18), 0, }; static unsigned int mc34704_reg_written[MAX_MC34704_REG / 32 + 1]; static unsigned char mc34704_shadow_regs[MAX_MC34704_REG - 1]; #define IS_READONLY(r) ((1 << ((r) % 32)) & mc34704_reg_readonly[(r) / 32]) #define WAS_WRITTEN(r) ((1 << ((r) % 32)) & mc34704_reg_written[(r) / 32]) #define MARK_WRITTEN(r) do { \ mc34704_reg_written[(r) / 32] |= (1 << ((r) % 32)); \ } while (0) int pmic_read(int reg_nr, unsigned int *reg_val) { int c; /* * Use the shadow register if we've written to it */ if (WAS_WRITTEN(reg_nr)) { *reg_val = mc34704_shadow_regs[reg_nr]; return PMIC_SUCCESS; } /* * Otherwise, actually read the real register. * Write-only registers will read as zero. */ c = i2c_smbus_read_byte_data(mc34704_client, reg_nr); if (c == -1) { pr_debug("mc34704: error reading register 0x%02x\n", reg_nr); return PMIC_ERROR; } else { *reg_val = c; return PMIC_SUCCESS; } } int pmic_write(int reg_nr, const unsigned int reg_val) { int ret; ret = i2c_smbus_write_byte_data(mc34704_client, reg_nr, reg_val); if (ret == -1) { return PMIC_ERROR; } else { /* * Update our software copy of the register since you * can't read what you wrote. */ if (!IS_READONLY(reg_nr)) { mc34704_shadow_regs[reg_nr] = reg_val; MARK_WRITTEN(reg_nr); } return PMIC_SUCCESS; } } unsigned int pmic_get_active_events(unsigned int *active_events) { unsigned int count = 0; unsigned int faults; int bit_set; /* Check for any relevant PMIC faults */ pmic_read(REG_MC34704_FAULTS, &faults); faults &= events_enabled; /* * Mask all active events, because there is no way to acknowledge * or dismiss them in the PMIC -- they're sticky. */ events_enabled &= ~faults; /* Account for all unmasked faults */ while (faults) { bit_set = ffs(faults) - 1; *(active_events + count) = bit_set; count++; faults ^= (1 << bit_set); } return count; } int pmic_event_unmask(type_event event) { unsigned int event_bit = 0; unsigned int prior_events = events_enabled; event_bit = (1 << event); events_enabled |= event_bit; pr_debug("Enable Event : %d\n", event); /* start the polling task as needed */ if (events_enabled && prior_events == 0) pmic_trigger_poll(); return 0; } int pmic_event_mask(type_event event) { unsigned int event_bit = 0; event_bit = (1 << event); events_enabled &= ~event_bit; pr_debug("Disable Event : %d\n", event); return 0; } /*! * PMIC event polling task. This task is called periodically to poll * for possible MC34704 events (No interrupt supplied by the hardware). */ static void pmic_event_task(struct work_struct *work); DECLARE_DELAYED_WORK(pmic_ws, pmic_event_task); static void pmic_trigger_poll(void) { schedule_delayed_work(&pmic_ws, HZ / 10); } static void pmic_event_task(struct work_struct *work) { unsigned int count = 0; int i; count = pmic_get_active_events(active_events); pr_debug("active events number %d\n", count); /* call handlers for all active events */ for (i = 0; i < count; i++) pmic_event_callback(active_events[i]); /* re-trigger this task, but only if somebody is watching */ if (events_enabled) pmic_trigger_poll(); return; } pmic_version_t pmic_get_version(void) { return mxc_pmic_version; } EXPORT_SYMBOL(pmic_get_version); int __devinit pmic_init_registers(void) { /* * Set some registers to what they should be, * if for no other reason than to initialize our * software register copies. */ CHECK_ERROR(pmic_write(REG_MC34704_GENERAL2, 0x09)); CHECK_ERROR(pmic_write(REG_MC34704_VGSET1, 0)); CHECK_ERROR(pmic_write(REG_MC34704_REG2SET1, 0)); CHECK_ERROR(pmic_write(REG_MC34704_REG3SET1, 0)); CHECK_ERROR(pmic_write(REG_MC34704_REG4SET1, 0)); CHECK_ERROR(pmic_write(REG_MC34704_REG5SET1, 0)); return PMIC_SUCCESS; } static int __devinit is_chip_onboard(struct i2c_client *client) { int val; /* * This PMIC has no version or ID register, so just see * if it ACK's and returns 0 on some write-only register as * evidence of its presence. */ val = i2c_smbus_read_byte_data(client, REG_MC34704_GENERAL2); if (val != 0) return -1; return 0; } static int __devinit pmic_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret = 0; struct mc34704 *mc34704; struct mc34704_platform_data *plat_data = client->dev.platform_data; if (!plat_data || !plat_data->init) return -ENODEV; ret = is_chip_onboard(client); if (ret == -1) return -ENODEV; mc34704 = kzalloc(sizeof(struct mc34704), GFP_KERNEL); if (mc34704 == NULL) return -ENOMEM; i2c_set_clientdata(client, mc34704); mc34704->dev = &client->dev; mc34704->i2c_client = client; mc34704_client = client; /* Initialize the PMIC event handling */ pmic_event_list_init(); /* Initialize PMI registers */ if (pmic_init_registers() != PMIC_SUCCESS) return PMIC_ERROR; ret = plat_data->init(mc34704); if (ret != 0) return PMIC_ERROR; dev_info(&client->dev, "Loaded\n"); return PMIC_SUCCESS; } static int pmic_remove(struct i2c_client *client) { return 0; } static int pmic_suspend(struct i2c_client *client, pm_message_t state) { return 0; } static int pmic_resume(struct i2c_client *client) { return 0; } static const struct i2c_device_id mc34704_id[] = { {"mc34704", 0}, {}, }; MODULE_DEVICE_TABLE(i2c, mc34704_id); static struct i2c_driver pmic_driver = { .driver = { .name = "mc34704", .bus = NULL, }, .probe = pmic_probe, .remove = pmic_remove, .suspend = pmic_suspend, .resume = pmic_resume, .id_table = mc34704_id, }; static int __init pmic_init(void) { return i2c_add_driver(&pmic_driver); } static void __exit pmic_exit(void) { i2c_del_driver(&pmic_driver); } /* * Module entry points */ subsys_initcall_sync(pmic_init); module_exit(pmic_exit); MODULE_DESCRIPTION("MC34704 PMIC driver"); MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_LICENSE("GPL");