[PATCH] Added i2c support for mc13xxx-core
Yong Shen
yong.shen at linaro.org
Wed Dec 8 01:24:01 EST 2010
Hi Marc,
It's better to use git format-patch to gernerate patch for review, by
which you can add some commit information and s-o-b stuff.
Please see inline comments.
On Tue, Dec 7, 2010 at 3:14 PM, Marc Reilly <marc at cpdesign.com.au> wrote:
> ---
> drivers/mfd/mc13xxx-core.c | 332 +++++++++++++++++++++++++++++++-------------
> 1 files changed, 234 insertions(+), 98 deletions(-)
>
> diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c
> index a2ac2ed..229cf43 100644
> --- a/drivers/mfd/mc13xxx-core.c
> +++ b/drivers/mfd/mc13xxx-core.c
> @@ -9,7 +9,6 @@
> * the terms of the GNU General Public License version 2 as published by the
> * Free Software Foundation.
> */
> -
> #include <linux/slab.h>
> #include <linux/module.h>
> #include <linux/platform_device.h>
> @@ -18,12 +17,28 @@
> #include <linux/spi/spi.h>
> #include <linux/mfd/core.h>
> #include <linux/mfd/mc13xxx.h>
> +#include <linux/i2c.h>
> +
> +enum mc13xxx_id {
> + MC13XXX_ID_MC13783,
> + MC13XXX_ID_MC13892,
> + MC13XXX_ID_INVALID,
> +};
>
> struct mc13xxx {
> - struct spi_device *spidev;
> + union {
> + struct spi_device *spidev;
> + struct i2c_client* client;
> + };
> + struct device *pdev;
> + enum mc13xxx_id icid;
> +
> struct mutex lock;
> int irq;
>
> + int (*read_dev)(struct mc13xxx*, unsigned int, u32*);
> + int (*write_dev)(struct mc13xxx*, unsigned int, u32);
> +
> irq_handler_t irqhandler[MC13XXX_NUM_IRQ];
> void *irqdata[MC13XXX_NUM_IRQ];
> };
> @@ -150,36 +165,46 @@ EXPORT_SYMBOL(mc13783_to_mc13xxx);
> void mc13xxx_lock(struct mc13xxx *mc13xxx)
> {
> if (!mutex_trylock(&mc13xxx->lock)) {
> - dev_dbg(&mc13xxx->spidev->dev, "wait for %s from %pf\n",
> + dev_dbg(mc13xxx->pdev, "wait for %s from %pf\n",
> __func__, __builtin_return_address(0));
>
> mutex_lock(&mc13xxx->lock);
> }
> - dev_dbg(&mc13xxx->spidev->dev, "%s from %pf\n",
> + dev_dbg(mc13xxx->pdev, "%s from %pf\n",
> __func__, __builtin_return_address(0));
> }
> EXPORT_SYMBOL(mc13xxx_lock);
>
> void mc13xxx_unlock(struct mc13xxx *mc13xxx)
> {
> - dev_dbg(&mc13xxx->spidev->dev, "%s from %pf\n",
> + dev_dbg(mc13xxx->pdev, "%s from %pf\n",
> __func__, __builtin_return_address(0));
> mutex_unlock(&mc13xxx->lock);
> }
> EXPORT_SYMBOL(mc13xxx_unlock);
>
> -#define MC13XXX_REGOFFSET_SHIFT 25
> int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val)
> {
> - struct spi_transfer t;
> - struct spi_message m;
> int ret;
> -
> BUG_ON(!mutex_is_locked(&mc13xxx->lock));
>
> if (offset > MC13XXX_NUMREGS)
> return -EINVAL;
>
> + ret = mc13xxx->read_dev(mc13xxx, offset, val);
> + dev_vdbg(mc13xxx->pdev, "[0x%02x] -> 0x%06x\n", offset, *val);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(mc13xxx_reg_read);
> +
> +#define MC13XXX_REGOFFSET_SHIFT 25
> +static int mc13xxx_spi_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val)
> +{
> + struct spi_transfer t;
> + struct spi_message m;
> + int ret;
> +
> *val = offset << MC13XXX_REGOFFSET_SHIFT;
>
> memset(&t, 0, sizeof(t));
> @@ -201,26 +226,28 @@ int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val)
>
> *val &= 0xffffff;
>
> - dev_vdbg(&mc13xxx->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
> -
> return 0;
> }
> -EXPORT_SYMBOL(mc13xxx_reg_read);
>
> int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val)
> {
> - u32 buf;
> - struct spi_transfer t;
> - struct spi_message m;
> - int ret;
> -
> BUG_ON(!mutex_is_locked(&mc13xxx->lock));
> -
> - dev_vdbg(&mc13xxx->spidev->dev, "[0x%02x] <- 0x%06x\n", offset, val);
> + dev_vdbg(mc13xxx->pdev, "[0x%02x] <- 0x%06x\n", offset, val);
>
> if (offset > MC13XXX_NUMREGS || val > 0xffffff)
> return -EINVAL;
>
> + return mc13xxx->write_dev(mc13xxx, offset, val);
> +}
> +EXPORT_SYMBOL(mc13xxx_reg_write);
> +
> +static int mc13xxx_spi_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val)
> +{
> + u32 buf;
> + struct spi_transfer t;
> + struct spi_message m;
> + int ret;
> +
> buf = 1 << 31 | offset << MC13XXX_REGOFFSET_SHIFT | val;
>
> memset(&t, 0, sizeof(t));
> @@ -241,7 +268,33 @@ int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val)
>
> return 0;
> }
> -EXPORT_SYMBOL(mc13xxx_reg_write);
> +
> +static int mc13xxx_i2c_reg_read(struct mc13xxx *mc13xxx, unsigned int offset,
> + u32 *val)
> +{
> + int ret;
> + unsigned char buff[3] = {0,0,0};
> +
> + ret = i2c_smbus_read_i2c_block_data(mc13xxx->client, offset, 3, buff);
> + *val = buff[0] << 16 | buff[1] << 8 | buff[2];
> +
> + return ret == 3 ? 0 : ret;
> +}
> +
> +static int mc13xxx_i2c_reg_write(struct mc13xxx *mc13xxx, unsigned int offset,
> + u32 val)
> +{
> + int ret;
> + unsigned char buff[3];
> +
> + buff[0] = (val >> 16) & 0xff;
> + buff[1] = (val >> 8) & 0xff;
> + buff[2] = val & 0xff;
> +
> + ret = i2c_smbus_write_i2c_block_data(mc13xxx->client, offset, 3, buff);
> +
> + return ret;
> +}
>
> int mc13xxx_reg_rmw(struct mc13xxx *mc13xxx, unsigned int offset,
> u32 mask, u32 val)
> @@ -445,7 +498,7 @@ static int mc13xxx_irq_handle(struct mc13xxx *mc13xxx,
> if (handled == IRQ_HANDLED)
> num_handled++;
> } else {
> - dev_err(&mc13xxx->spidev->dev,
> + dev_err(mc13xxx->pdev,
> "BUG: irq %u but no handler\n",
> baseirq + irq);
>
> @@ -481,11 +534,6 @@ static irqreturn_t mc13xxx_irq_thread(int irq, void *data)
> return IRQ_RETVAL(handled);
> }
>
> -enum mc13xxx_id {
> - MC13XXX_ID_MC13783,
> - MC13XXX_ID_MC13892,
> - MC13XXX_ID_INVALID,
> -};
>
> const char *mc13xxx_chipname[] = {
> [MC13XXX_ID_MC13783] = "mc13783",
> @@ -493,41 +541,38 @@ const char *mc13xxx_chipname[] = {
> };
>
> #define maskval(reg, mask) (((reg) & (mask)) >> __ffs(mask))
> -static int mc13xxx_identify(struct mc13xxx *mc13xxx, enum mc13xxx_id *id)
> +static int mc13xxx_identify(struct mc13xxx *mc13xxx, int usespi)
> {
> u32 icid;
> u32 revision;
> const char *name;
> int ret;
>
> - ret = mc13xxx_reg_read(mc13xxx, 46, &icid);
> + ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision);
> if (ret)
> return ret;
>
> - icid = (icid >> 6) & 0x7;
> + icid = (revision >> 6) & 0x7;
>
> switch (icid) {
> case 2:
> - *id = MC13XXX_ID_MC13783;
> + mc13xxx->icid = MC13XXX_ID_MC13783;
> name = "mc13783";
> break;
> case 7:
> - *id = MC13XXX_ID_MC13892;
> + mc13xxx->icid = MC13XXX_ID_MC13892;
> name = "mc13892";
> break;
> default:
> - *id = MC13XXX_ID_INVALID;
> + mc13xxx->icid = MC13XXX_ID_INVALID;
> break;
> }
>
> - if (*id == MC13XXX_ID_MC13783 || *id == MC13XXX_ID_MC13892) {
> - ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision);
> - if (ret)
> - return ret;
> -
> - dev_info(&mc13xxx->spidev->dev, "%s: rev: %d.%d, "
> + if (mc13xxx->icid == MC13XXX_ID_MC13783 ||
> + mc13xxx->icid == MC13XXX_ID_MC13892) {
> + dev_info(mc13xxx->pdev, "%s: rev: %d.%d, "
> "fin: %d, fab: %d, icid: %d/%d\n",
> - mc13xxx_chipname[*id],
> + mc13xxx_chipname[mc13xxx->icid],
> maskval(revision, MC13XXX_REVISION_REVFULL),
> maskval(revision, MC13XXX_REVISION_REVMETAL),
> maskval(revision, MC13XXX_REVISION_FIN),
> @@ -535,27 +580,23 @@ static int mc13xxx_identify(struct mc13xxx *mc13xxx, enum mc13xxx_id *id)
> maskval(revision, MC13XXX_REVISION_ICID),
> maskval(revision, MC13XXX_REVISION_ICIDCODE));
> }
> -
> - if (*id != MC13XXX_ID_INVALID) {
> - const struct spi_device_id *devid =
> - spi_get_device_id(mc13xxx->spidev);
> - if (!devid || devid->driver_data != *id)
> - dev_warn(&mc13xxx->spidev->dev, "device id doesn't "
> - "match auto detection!\n");
> + if (mc13xxx->icid != MC13XXX_ID_INVALID) {
> + if (usespi) {
> + const struct spi_device_id *devid =
> + spi_get_device_id(mc13xxx->spidev);
> + if (!devid || devid->driver_data != mc13xxx->icid)
> + dev_warn(mc13xxx->pdev, "device id doesn't "
> + "match auto detection!\n");
> + } else {
> + /* TODO */
todo what? remove this or add real code here.
> + }
> }
> -
> return 0;
> }
>
> static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx)
> {
> - const struct spi_device_id *devid =
> - spi_get_device_id(mc13xxx->spidev);
> -
> - if (!devid)
> - return NULL;
> -
> - return mc13xxx_chipname[devid->driver_data];
> + return mc13xxx_chipname[mc13xxx->icid];
> }
>
> #include <linux/mfd/mc13783.h>
> @@ -563,7 +604,7 @@ static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx)
> int mc13xxx_get_flags(struct mc13xxx *mc13xxx)
> {
> struct mc13xxx_platform_data *pdata =
> - dev_get_platdata(&mc13xxx->spidev->dev);
> + dev_get_platdata(mc13xxx->pdev);
>
> return pdata->flags;
> }
> @@ -601,7 +642,7 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
> };
> init_completion(&adcdone_data.done);
>
> - dev_dbg(&mc13xxx->spidev->dev, "%s\n", __func__);
> + dev_dbg(mc13xxx->pdev, "%s\n", __func__);
>
> mc13xxx_lock(mc13xxx);
>
> @@ -643,7 +684,7 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
> return -EINVAL;
> }
>
> - dev_dbg(&mc13783->mc13xxx.spidev->dev, "%s: request irq\n", __func__);
> + dev_dbg(mc13783->mc13xxx.pdev, "%s: request irq\n", __func__);
> mc13xxx_irq_request(mc13xxx, MC13783_IRQ_ADCDONE,
> mc13783_handler_adcdone, __func__, &adcdone_data);
> mc13xxx_irq_ack(mc13xxx, MC13783_IRQ_ADCDONE);
> @@ -701,7 +742,7 @@ static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx,
> if (!cell.name)
> return -ENOMEM;
>
> - return mfd_add_devices(&mc13xxx->spidev->dev, -1, &cell, 1, NULL, 0);
> + return mfd_add_devices(mc13xxx->pdev, -1, &cell, 1, NULL, 0);
> }
>
> static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format)
> @@ -709,11 +750,48 @@ static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format)
> return mc13xxx_add_subdevice_pdata(mc13xxx, format, NULL, 0);
> }
>
> -static int mc13xxx_probe(struct spi_device *spi)
> +static int mc13xxx_common_probe(struct mc13xxx* mc13xxx,
> + struct mc13xxx_platform_data* pdata)
> +{
> + mc13xxx_unlock(mc13xxx);
> +
> + if (!pdata)
> + return 0;
> +
> + if (pdata->flags & MC13XXX_USE_ADC)
> + mc13xxx_add_subdevice(mc13xxx, "%s-adc");
> +
> + if (pdata->flags & MC13XXX_USE_CODEC)
> + mc13xxx_add_subdevice(mc13xxx, "%s-codec");
> +
> + if (pdata->flags & MC13XXX_USE_REGULATOR) {
> + struct mc13xxx_regulator_platform_data regulator_pdata = {
> + .num_regulators = pdata->num_regulators,
> + .regulators = pdata->regulators,
> + };
> +
> + mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",
> + ®ulator_pdata, sizeof(regulator_pdata));
> + }
> +
> + if (pdata->flags & MC13XXX_USE_RTC)
> + mc13xxx_add_subdevice(mc13xxx, "%s-rtc");
> +
> + if (pdata->flags & MC13XXX_USE_TOUCHSCREEN)
> + mc13xxx_add_subdevice(mc13xxx, "%s-ts");
> +
> + if (pdata->flags & MC13XXX_USE_LED) {
> + mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led",
> + pdata->leds, sizeof(*pdata->leds));
> + }
> +
> + return 0;
> +}
> +
> +static int mc13xxx_spi_probe(struct spi_device *spi)
> {
> struct mc13xxx *mc13xxx;
> struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev);
> - enum mc13xxx_id id;
> int ret;
>
> mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
> @@ -725,13 +803,17 @@ static int mc13xxx_probe(struct spi_device *spi)
> spi->bits_per_word = 32;
> spi_setup(spi);
>
> + mc13xxx->pdev = &spi->dev;
> mc13xxx->spidev = spi;
> + mc13xxx->read_dev = mc13xxx_spi_reg_read;
> + mc13xxx->write_dev = mc13xxx_spi_reg_write;
> + mc13xxx->irq = spi->irq;
>
> mutex_init(&mc13xxx->lock);
> mc13xxx_lock(mc13xxx);
>
> - ret = mc13xxx_identify(mc13xxx, &id);
> - if (ret || id == MC13XXX_ID_INVALID)
> + ret = mc13xxx_identify(mc13xxx, 1);
> + if (ret || (mc13xxx->icid == MC13XXX_ID_INVALID))
> goto err_revision;
>
> /* mask all irqs */
> @@ -743,7 +825,7 @@ static int mc13xxx_probe(struct spi_device *spi)
> if (ret)
> goto err_mask;
>
> - ret = request_threaded_irq(spi->irq, NULL, mc13xxx_irq_thread,
> + ret = request_threaded_irq(mc13xxx->irq, NULL, mc13xxx_irq_thread,
> IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13xxx", mc13xxx);
>
> if (ret) {
> @@ -755,43 +837,15 @@ err_revision:
> return ret;
> }
>
> - mc13xxx_unlock(mc13xxx);
> -
> - if (pdata->flags & MC13XXX_USE_ADC)
> - mc13xxx_add_subdevice(mc13xxx, "%s-adc");
> -
> - if (pdata->flags & MC13XXX_USE_CODEC)
> - mc13xxx_add_subdevice(mc13xxx, "%s-codec");
> -
> - if (pdata->flags & MC13XXX_USE_REGULATOR) {
> - struct mc13xxx_regulator_platform_data regulator_pdata = {
> - .num_regulators = pdata->num_regulators,
> - .regulators = pdata->regulators,
> - };
> -
> - mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",
> - ®ulator_pdata, sizeof(regulator_pdata));
> - }
> -
> - if (pdata->flags & MC13XXX_USE_RTC)
> - mc13xxx_add_subdevice(mc13xxx, "%s-rtc");
> -
> - if (pdata->flags & MC13XXX_USE_TOUCHSCREEN)
> - mc13xxx_add_subdevice(mc13xxx, "%s-ts");
> -
> - if (pdata->flags & MC13XXX_USE_LED) {
> - mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led",
> - pdata->leds, sizeof(*pdata->leds));
> - }
> -
> - return 0;
> + return mc13xxx_common_probe(mc13xxx, pdata);
> }
>
> -static int __devexit mc13xxx_remove(struct spi_device *spi)
> +
> +static int __devexit mc13xxx_spi_remove(struct spi_device *spi)
> {
> struct mc13xxx *mc13xxx = dev_get_drvdata(&spi->dev);
>
> - free_irq(mc13xxx->spidev->irq, mc13xxx);
> + free_irq(mc13xxx->irq, mc13xxx);
>
> mfd_remove_devices(&spi->dev);
>
> @@ -819,18 +873,100 @@ static struct spi_driver mc13xxx_driver = {
> .bus = &spi_bus_type,
> .owner = THIS_MODULE,
> },
> - .probe = mc13xxx_probe,
> - .remove = __devexit_p(mc13xxx_remove),
> + .probe = mc13xxx_spi_probe,
> + .remove = __devexit_p(mc13xxx_spi_remove),
> +};
> +
> +static int mc13xxx_i2c_probe(struct i2c_client *client,
> + const struct i2c_device_id* id)
> +{
> + struct mc13xxx *mc13xxx;
> + struct mc13xxx_platform_data *pdata = dev_get_platdata(&client->dev);
> + int ret;
> +
> + mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
> + if (!mc13xxx)
> + return -ENOMEM;
> +
> + dev_set_drvdata(&client->dev, mc13xxx);
> + mc13xxx->pdev = &client->dev;
> + mc13xxx->client = client;
> + mc13xxx->read_dev = mc13xxx_i2c_reg_read;
> + mc13xxx->write_dev = mc13xxx_i2c_reg_write;
> + mc13xxx->irq = client->irq;
> +
> + mutex_init(&mc13xxx->lock);
> + mc13xxx_lock(mc13xxx);
> +
> + ret = mc13xxx_identify(mc13xxx, 0);
> + if (ret || (mc13xxx->icid == MC13XXX_ID_INVALID))
> + goto err_revision;
> +
> + /* mask all irqs */
> + ret = mc13xxx_reg_write(mc13xxx, MC13XXX_IRQMASK0, 0x00ffffff);
> + if (ret)
> + goto err_mask;
> +
> + ret = mc13xxx_reg_write(mc13xxx, MC13XXX_IRQMASK1, 0x00ffffff);
> + if (ret)
> + goto err_mask;
> +
> + ret = request_threaded_irq(mc13xxx->irq, NULL, mc13xxx_irq_thread,
> + IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13xxx", mc13xxx);
> +
> + if (ret) {
> +err_mask:
> +err_revision:
Why two labels here?
Also, if ret == 0, the lock will not be released.
> + mutex_unlock(&mc13xxx->lock);
> + kfree(mc13xxx);
> + return ret;
> + }
> +
> + return mc13xxx_common_probe(mc13xxx,pdata);
> +}
> +
> +static int __devexit mc13xxx_i2c_remove(struct i2c_client *client)
> +{
> + struct mc13xxx *mc13xxx = dev_get_drvdata(&client->dev);
> +
> + free_irq(mc13xxx->irq, mc13xxx);
> +
> + mfd_remove_devices(&client->dev);
> +
> + kfree(mc13xxx);
> +
> + return 0;
> +}
> +
> +static const struct i2c_device_id mc13xxx_i2c_idtable[] = {
> + { "mc13892", MC13XXX_ID_MC13892},
> + { }
you may want to remove empty '{}'
> +};
> +
> +static struct i2c_driver mc13xxx_i2c_driver = {
> + .driver = {
> + .owner = THIS_MODULE,
> + .name = "mc13xxx-i2c"
> + },
> + .id_table = mc13xxx_i2c_idtable,
> + .probe = mc13xxx_i2c_probe,
> + .remove = __devexit_p(mc13xxx_i2c_remove),
> };
>
> static int __init mc13xxx_init(void)
> {
> - return spi_register_driver(&mc13xxx_driver);
> + int i2cret;
> + int spiret;
> + i2cret = i2c_add_driver(&mc13xxx_i2c_driver);
> + spiret = spi_register_driver(&mc13xxx_driver);
> +
> + return (i2cret == 0) && (spiret == 0);
> }
> subsys_initcall(mc13xxx_init);
>
> static void __exit mc13xxx_exit(void)
> {
> + i2c_del_driver(&mc13xxx_i2c_driver);
> spi_unregister_driver(&mc13xxx_driver);
> }
> module_exit(mc13xxx_exit);
> --
> 1.7.1
>
>
More information about the linux-arm-kernel
mailing list