Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * I2C driver for Marvell 88PM80x
  3 *
  4 * Copyright (C) 2012 Marvell International Ltd.
  5 * Haojian Zhuang <haojian.zhuang@marvell.com>
  6 * Joseph(Yossi) Hanin <yhanin@marvell.com>
  7 * Qiao Zhou <zhouqiao@marvell.com>
  8 *
  9 * This program is free software; you can redistribute it and/or modify
 10 * it under the terms of the GNU General Public License version 2 as
 11 * published by the Free Software Foundation.
 12 */
 13#include <linux/kernel.h>
 14#include <linux/module.h>
 15#include <linux/i2c.h>
 16#include <linux/mfd/88pm80x.h>
 17#include <linux/slab.h>
 18#include <linux/uaccess.h>
 19#include <linux/err.h>
 20
 21/* 88pm80x chips have same definition for chip id register. */
 22#define PM80X_CHIP_ID			(0x00)
 23#define PM80X_CHIP_ID_NUM(x)		(((x) >> 5) & 0x7)
 24#define PM80X_CHIP_ID_REVISION(x)	((x) & 0x1F)
 25
 26struct pm80x_chip_mapping {
 27	unsigned int	id;
 28	int		type;
 29};
 30
 31static struct pm80x_chip_mapping chip_mapping[] = {
 32	/* 88PM800 chip id number */
 33	{0x3,	CHIP_PM800},
 34	/* 88PM805 chip id number */
 35	{0x0,	CHIP_PM805},
 36	/* 88PM860 chip id number */
 37	{0x4,	CHIP_PM860},
 38};
 39
 40/*
 41 * workaround: some registers needed by pm805 are defined in pm800, so
 42 * need to use this global variable to maintain the relation between
 43 * pm800 and pm805. would remove it after HW chip fixes the issue.
 44 */
 45static struct pm80x_chip *g_pm80x_chip;
 46
 47const struct regmap_config pm80x_regmap_config = {
 48	.reg_bits = 8,
 49	.val_bits = 8,
 50};
 51EXPORT_SYMBOL_GPL(pm80x_regmap_config);
 52
 53
 54int pm80x_init(struct i2c_client *client)
 55{
 56	struct pm80x_chip *chip;
 57	struct regmap *map;
 58	unsigned int val;
 59	int i, ret = 0;
 60
 61	chip =
 62	    devm_kzalloc(&client->dev, sizeof(struct pm80x_chip), GFP_KERNEL);
 63	if (!chip)
 64		return -ENOMEM;
 65
 66	map = devm_regmap_init_i2c(client, &pm80x_regmap_config);
 67	if (IS_ERR(map)) {
 68		ret = PTR_ERR(map);
 69		dev_err(&client->dev, "Failed to allocate register map: %d\n",
 70			ret);
 71		return ret;
 72	}
 73
 74	chip->client = client;
 75	chip->regmap = map;
 76
 77	chip->irq = client->irq;
 78
 79	chip->dev = &client->dev;
 80	dev_set_drvdata(chip->dev, chip);
 81	i2c_set_clientdata(chip->client, chip);
 82
 83	ret = regmap_read(chip->regmap, PM80X_CHIP_ID, &val);
 84	if (ret < 0) {
 85		dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
 86		return ret;
 87	}
 88
 89	for (i = 0; i < ARRAY_SIZE(chip_mapping); i++) {
 90		if (chip_mapping[i].id == PM80X_CHIP_ID_NUM(val)) {
 91			chip->type = chip_mapping[i].type;
 92			break;
 93		}
 94	}
 95
 96	if (i == ARRAY_SIZE(chip_mapping)) {
 97		dev_err(chip->dev,
 98			"Failed to detect Marvell 88PM800:ChipID[0x%x]\n", val);
 99		return -EINVAL;
100	}
101
102	device_init_wakeup(&client->dev, 1);
103
104	/*
105	 * workaround: set g_pm80x_chip to the first probed chip. if the
106	 * second chip is probed, just point to the companion to each
107	 * other so that pm805 can access those specific register. would
108	 * remove it after HW chip fixes the issue.
109	 */
110	if (!g_pm80x_chip)
111		g_pm80x_chip = chip;
112	else {
113		chip->companion = g_pm80x_chip->client;
114		g_pm80x_chip->companion = chip->client;
115	}
116
117	return 0;
118}
119EXPORT_SYMBOL_GPL(pm80x_init);
120
121int pm80x_deinit(void)
122{
123	/*
124	 * workaround: clear the dependency between pm800 and pm805.
125	 * would remove it after HW chip fixes the issue.
126	 */
127	if (g_pm80x_chip->companion)
128		g_pm80x_chip->companion = NULL;
129	else
130		g_pm80x_chip = NULL;
131	return 0;
132}
133EXPORT_SYMBOL_GPL(pm80x_deinit);
134
135#ifdef CONFIG_PM_SLEEP
136static int pm80x_suspend(struct device *dev)
137{
138	struct i2c_client *client = to_i2c_client(dev);
139	struct pm80x_chip *chip = i2c_get_clientdata(client);
140
141	if (chip && chip->wu_flag)
142		if (device_may_wakeup(chip->dev))
143			enable_irq_wake(chip->irq);
144
145	return 0;
146}
147
148static int pm80x_resume(struct device *dev)
149{
150	struct i2c_client *client = to_i2c_client(dev);
151	struct pm80x_chip *chip = i2c_get_clientdata(client);
152
153	if (chip && chip->wu_flag)
154		if (device_may_wakeup(chip->dev))
155			disable_irq_wake(chip->irq);
156
157	return 0;
158}
159#endif
160
161SIMPLE_DEV_PM_OPS(pm80x_pm_ops, pm80x_suspend, pm80x_resume);
162EXPORT_SYMBOL_GPL(pm80x_pm_ops);
163
164MODULE_DESCRIPTION("I2C Driver for Marvell 88PM80x");
165MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
166MODULE_LICENSE("GPL");