131dd6a2672e337f5de188df3e5169ee732798236Todd Fischer/* 231dd6a2672e337f5de188df3e5169ee732798236Todd Fischer * tps6507x.c -- TPS6507x chip family multi-function driver 331dd6a2672e337f5de188df3e5169ee732798236Todd Fischer * 431dd6a2672e337f5de188df3e5169ee732798236Todd Fischer * Copyright (c) 2010 RidgeRun (todd.fischer@ridgerun.com) 531dd6a2672e337f5de188df3e5169ee732798236Todd Fischer * 631dd6a2672e337f5de188df3e5169ee732798236Todd Fischer * Author: Todd Fischer 731dd6a2672e337f5de188df3e5169ee732798236Todd Fischer * todd.fischer@ridgerun.com 831dd6a2672e337f5de188df3e5169ee732798236Todd Fischer * 931dd6a2672e337f5de188df3e5169ee732798236Todd Fischer * Credits: 1031dd6a2672e337f5de188df3e5169ee732798236Todd Fischer * 1131dd6a2672e337f5de188df3e5169ee732798236Todd Fischer * Using code from wm831x-*.c, wm8400-core, Wolfson Microelectronics PLC. 1231dd6a2672e337f5de188df3e5169ee732798236Todd Fischer * 1331dd6a2672e337f5de188df3e5169ee732798236Todd Fischer * For licencing details see kernel-base/COPYING 1431dd6a2672e337f5de188df3e5169ee732798236Todd Fischer * 1531dd6a2672e337f5de188df3e5169ee732798236Todd Fischer */ 1631dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 1731dd6a2672e337f5de188df3e5169ee732798236Todd Fischer#include <linux/module.h> 1831dd6a2672e337f5de188df3e5169ee732798236Todd Fischer#include <linux/moduleparam.h> 1931dd6a2672e337f5de188df3e5169ee732798236Todd Fischer#include <linux/init.h> 2031dd6a2672e337f5de188df3e5169ee732798236Todd Fischer#include <linux/slab.h> 2131dd6a2672e337f5de188df3e5169ee732798236Todd Fischer#include <linux/i2c.h> 2231dd6a2672e337f5de188df3e5169ee732798236Todd Fischer#include <linux/mfd/core.h> 2331dd6a2672e337f5de188df3e5169ee732798236Todd Fischer#include <linux/mfd/tps6507x.h> 2431dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 2531dd6a2672e337f5de188df3e5169ee732798236Todd Fischerstatic struct mfd_cell tps6507x_devs[] = { 2631dd6a2672e337f5de188df3e5169ee732798236Todd Fischer { 2731dd6a2672e337f5de188df3e5169ee732798236Todd Fischer .name = "tps6507x-pmic", 2831dd6a2672e337f5de188df3e5169ee732798236Todd Fischer }, 29752599667048898b6969e06e4637f906b04ec752Todd Fischer { 30752599667048898b6969e06e4637f906b04ec752Todd Fischer .name = "tps6507x-ts", 31752599667048898b6969e06e4637f906b04ec752Todd Fischer }, 3231dd6a2672e337f5de188df3e5169ee732798236Todd Fischer}; 3331dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 3431dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 3531dd6a2672e337f5de188df3e5169ee732798236Todd Fischerstatic int tps6507x_i2c_read_device(struct tps6507x_dev *tps6507x, char reg, 3631dd6a2672e337f5de188df3e5169ee732798236Todd Fischer int bytes, void *dest) 3731dd6a2672e337f5de188df3e5169ee732798236Todd Fischer{ 3831dd6a2672e337f5de188df3e5169ee732798236Todd Fischer struct i2c_client *i2c = tps6507x->i2c_client; 3931dd6a2672e337f5de188df3e5169ee732798236Todd Fischer struct i2c_msg xfer[2]; 4031dd6a2672e337f5de188df3e5169ee732798236Todd Fischer int ret; 4131dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 4231dd6a2672e337f5de188df3e5169ee732798236Todd Fischer /* Write register */ 4331dd6a2672e337f5de188df3e5169ee732798236Todd Fischer xfer[0].addr = i2c->addr; 4431dd6a2672e337f5de188df3e5169ee732798236Todd Fischer xfer[0].flags = 0; 4531dd6a2672e337f5de188df3e5169ee732798236Todd Fischer xfer[0].len = 1; 4631dd6a2672e337f5de188df3e5169ee732798236Todd Fischer xfer[0].buf = ® 4731dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 4831dd6a2672e337f5de188df3e5169ee732798236Todd Fischer /* Read data */ 4931dd6a2672e337f5de188df3e5169ee732798236Todd Fischer xfer[1].addr = i2c->addr; 5031dd6a2672e337f5de188df3e5169ee732798236Todd Fischer xfer[1].flags = I2C_M_RD; 5131dd6a2672e337f5de188df3e5169ee732798236Todd Fischer xfer[1].len = bytes; 5231dd6a2672e337f5de188df3e5169ee732798236Todd Fischer xfer[1].buf = dest; 5331dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 5431dd6a2672e337f5de188df3e5169ee732798236Todd Fischer ret = i2c_transfer(i2c->adapter, xfer, 2); 5531dd6a2672e337f5de188df3e5169ee732798236Todd Fischer if (ret == 2) 5631dd6a2672e337f5de188df3e5169ee732798236Todd Fischer ret = 0; 5731dd6a2672e337f5de188df3e5169ee732798236Todd Fischer else if (ret >= 0) 5831dd6a2672e337f5de188df3e5169ee732798236Todd Fischer ret = -EIO; 5931dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 6031dd6a2672e337f5de188df3e5169ee732798236Todd Fischer return ret; 6131dd6a2672e337f5de188df3e5169ee732798236Todd Fischer} 6231dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 6331dd6a2672e337f5de188df3e5169ee732798236Todd Fischerstatic int tps6507x_i2c_write_device(struct tps6507x_dev *tps6507x, char reg, 6431dd6a2672e337f5de188df3e5169ee732798236Todd Fischer int bytes, void *src) 6531dd6a2672e337f5de188df3e5169ee732798236Todd Fischer{ 6631dd6a2672e337f5de188df3e5169ee732798236Todd Fischer struct i2c_client *i2c = tps6507x->i2c_client; 6731dd6a2672e337f5de188df3e5169ee732798236Todd Fischer /* we add 1 byte for device register */ 6831dd6a2672e337f5de188df3e5169ee732798236Todd Fischer u8 msg[TPS6507X_MAX_REGISTER + 1]; 6931dd6a2672e337f5de188df3e5169ee732798236Todd Fischer int ret; 7031dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 71a8d6aa08dd3e004df6a6e63a61698d834a191ad3Axel Lin if (bytes > TPS6507X_MAX_REGISTER) 7231dd6a2672e337f5de188df3e5169ee732798236Todd Fischer return -EINVAL; 7331dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 7431dd6a2672e337f5de188df3e5169ee732798236Todd Fischer msg[0] = reg; 7531dd6a2672e337f5de188df3e5169ee732798236Todd Fischer memcpy(&msg[1], src, bytes); 7631dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 7731dd6a2672e337f5de188df3e5169ee732798236Todd Fischer ret = i2c_master_send(i2c, msg, bytes + 1); 7831dd6a2672e337f5de188df3e5169ee732798236Todd Fischer if (ret < 0) 7931dd6a2672e337f5de188df3e5169ee732798236Todd Fischer return ret; 8031dd6a2672e337f5de188df3e5169ee732798236Todd Fischer if (ret != bytes + 1) 8131dd6a2672e337f5de188df3e5169ee732798236Todd Fischer return -EIO; 8231dd6a2672e337f5de188df3e5169ee732798236Todd Fischer return 0; 8331dd6a2672e337f5de188df3e5169ee732798236Todd Fischer} 8431dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 8531dd6a2672e337f5de188df3e5169ee732798236Todd Fischerstatic int tps6507x_i2c_probe(struct i2c_client *i2c, 8631dd6a2672e337f5de188df3e5169ee732798236Todd Fischer const struct i2c_device_id *id) 8731dd6a2672e337f5de188df3e5169ee732798236Todd Fischer{ 8831dd6a2672e337f5de188df3e5169ee732798236Todd Fischer struct tps6507x_dev *tps6507x; 8931dd6a2672e337f5de188df3e5169ee732798236Todd Fischer int ret = 0; 9031dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 9131dd6a2672e337f5de188df3e5169ee732798236Todd Fischer tps6507x = kzalloc(sizeof(struct tps6507x_dev), GFP_KERNEL); 9204a064236da3d8db24232983f99cfcfa5a4e5adeAxel Lin if (tps6507x == NULL) 9331dd6a2672e337f5de188df3e5169ee732798236Todd Fischer return -ENOMEM; 9431dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 9531dd6a2672e337f5de188df3e5169ee732798236Todd Fischer i2c_set_clientdata(i2c, tps6507x); 9631dd6a2672e337f5de188df3e5169ee732798236Todd Fischer tps6507x->dev = &i2c->dev; 9731dd6a2672e337f5de188df3e5169ee732798236Todd Fischer tps6507x->i2c_client = i2c; 9831dd6a2672e337f5de188df3e5169ee732798236Todd Fischer tps6507x->read_dev = tps6507x_i2c_read_device; 9931dd6a2672e337f5de188df3e5169ee732798236Todd Fischer tps6507x->write_dev = tps6507x_i2c_write_device; 10031dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 10131dd6a2672e337f5de188df3e5169ee732798236Todd Fischer ret = mfd_add_devices(tps6507x->dev, -1, 10231dd6a2672e337f5de188df3e5169ee732798236Todd Fischer tps6507x_devs, ARRAY_SIZE(tps6507x_devs), 10331dd6a2672e337f5de188df3e5169ee732798236Todd Fischer NULL, 0); 10431dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 10531dd6a2672e337f5de188df3e5169ee732798236Todd Fischer if (ret < 0) 10631dd6a2672e337f5de188df3e5169ee732798236Todd Fischer goto err; 10731dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 10831dd6a2672e337f5de188df3e5169ee732798236Todd Fischer return ret; 10931dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 11031dd6a2672e337f5de188df3e5169ee732798236Todd Fischererr: 11131dd6a2672e337f5de188df3e5169ee732798236Todd Fischer mfd_remove_devices(tps6507x->dev); 11231dd6a2672e337f5de188df3e5169ee732798236Todd Fischer kfree(tps6507x); 11331dd6a2672e337f5de188df3e5169ee732798236Todd Fischer return ret; 11431dd6a2672e337f5de188df3e5169ee732798236Todd Fischer} 11531dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 11631dd6a2672e337f5de188df3e5169ee732798236Todd Fischerstatic int tps6507x_i2c_remove(struct i2c_client *i2c) 11731dd6a2672e337f5de188df3e5169ee732798236Todd Fischer{ 11831dd6a2672e337f5de188df3e5169ee732798236Todd Fischer struct tps6507x_dev *tps6507x = i2c_get_clientdata(i2c); 11931dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 12031dd6a2672e337f5de188df3e5169ee732798236Todd Fischer mfd_remove_devices(tps6507x->dev); 12131dd6a2672e337f5de188df3e5169ee732798236Todd Fischer kfree(tps6507x); 12231dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 12331dd6a2672e337f5de188df3e5169ee732798236Todd Fischer return 0; 12431dd6a2672e337f5de188df3e5169ee732798236Todd Fischer} 12531dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 12631dd6a2672e337f5de188df3e5169ee732798236Todd Fischerstatic const struct i2c_device_id tps6507x_i2c_id[] = { 12731dd6a2672e337f5de188df3e5169ee732798236Todd Fischer { "tps6507x", 0 }, 12831dd6a2672e337f5de188df3e5169ee732798236Todd Fischer { } 12931dd6a2672e337f5de188df3e5169ee732798236Todd Fischer}; 13031dd6a2672e337f5de188df3e5169ee732798236Todd FischerMODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id); 13131dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 13231dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 13331dd6a2672e337f5de188df3e5169ee732798236Todd Fischerstatic struct i2c_driver tps6507x_i2c_driver = { 13431dd6a2672e337f5de188df3e5169ee732798236Todd Fischer .driver = { 13531dd6a2672e337f5de188df3e5169ee732798236Todd Fischer .name = "tps6507x", 13631dd6a2672e337f5de188df3e5169ee732798236Todd Fischer .owner = THIS_MODULE, 13731dd6a2672e337f5de188df3e5169ee732798236Todd Fischer }, 13831dd6a2672e337f5de188df3e5169ee732798236Todd Fischer .probe = tps6507x_i2c_probe, 13931dd6a2672e337f5de188df3e5169ee732798236Todd Fischer .remove = tps6507x_i2c_remove, 14031dd6a2672e337f5de188df3e5169ee732798236Todd Fischer .id_table = tps6507x_i2c_id, 14131dd6a2672e337f5de188df3e5169ee732798236Todd Fischer}; 14231dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 14331dd6a2672e337f5de188df3e5169ee732798236Todd Fischerstatic int __init tps6507x_i2c_init(void) 14431dd6a2672e337f5de188df3e5169ee732798236Todd Fischer{ 14531dd6a2672e337f5de188df3e5169ee732798236Todd Fischer return i2c_add_driver(&tps6507x_i2c_driver); 14631dd6a2672e337f5de188df3e5169ee732798236Todd Fischer} 14731dd6a2672e337f5de188df3e5169ee732798236Todd Fischer/* init early so consumer devices can complete system boot */ 14831dd6a2672e337f5de188df3e5169ee732798236Todd Fischersubsys_initcall(tps6507x_i2c_init); 14931dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 15031dd6a2672e337f5de188df3e5169ee732798236Todd Fischerstatic void __exit tps6507x_i2c_exit(void) 15131dd6a2672e337f5de188df3e5169ee732798236Todd Fischer{ 15231dd6a2672e337f5de188df3e5169ee732798236Todd Fischer i2c_del_driver(&tps6507x_i2c_driver); 15331dd6a2672e337f5de188df3e5169ee732798236Todd Fischer} 15431dd6a2672e337f5de188df3e5169ee732798236Todd Fischermodule_exit(tps6507x_i2c_exit); 15531dd6a2672e337f5de188df3e5169ee732798236Todd Fischer 15631dd6a2672e337f5de188df3e5169ee732798236Todd FischerMODULE_DESCRIPTION("TPS6507x chip family multi-function driver"); 15731dd6a2672e337f5de188df3e5169ee732798236Todd FischerMODULE_LICENSE("GPL"); 158