diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2010-10-10 19:36:19 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2010-10-11 13:22:13 +0200 |
commit | 2ebb440e1d92c60f47a0d89b7666f190caf38561 (patch) | |
tree | 3e361fe34257ce37463dec5b6d129bc52258fb7b /drivers/mfd | |
parent | 8f14d065e1e39d88949ee489acfe6f7faa51f1b1 (diff) | |
download | barebox-2ebb440e1d92c60f47a0d89b7666f190caf38561.tar.gz barebox-2ebb440e1d92c60f47a0d89b7666f190caf38561.tar.xz |
Move mfd drivers to drivers/mfd
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/mfd')
-rw-r--r-- | drivers/mfd/Kconfig | 28 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 6 | ||||
-rw-r--r-- | drivers/mfd/lp3972.c | 110 | ||||
-rw-r--r-- | drivers/mfd/mc13783.c | 237 | ||||
-rw-r--r-- | drivers/mfd/mc13892.c | 164 | ||||
-rw-r--r-- | drivers/mfd/mc34704.c | 140 | ||||
-rw-r--r-- | drivers/mfd/mc9sdz60.c | 153 | ||||
-rw-r--r-- | drivers/mfd/twl4030.c | 186 |
8 files changed, 1024 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig new file mode 100644 index 0000000000..7c27418bf1 --- /dev/null +++ b/drivers/mfd/Kconfig @@ -0,0 +1,28 @@ +menu MFD + +config I2C_MC13892 + depends on I2C + bool "MC13892 a.k.a. PMIC driver" + +config I2C_MC34704 + depends on I2C + bool "MC34704 PMIC driver" + +config I2C_MC9SDZ60 + depends on I2C + bool "MC9SDZ60 driver" + +config I2C_LP3972 + depends on I2C + bool "LP3972 driver" + +config I2C_TWL4030 + depends on I2C + bool "TWL4030 driver" + select GPIO + +config DRIVER_SPI_MC13783 + depends on SPI + bool "MC13783 a.k.a. PMIC driver" + +endmenu diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile new file mode 100644 index 0000000000..d411f23b69 --- /dev/null +++ b/drivers/mfd/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_I2C_MC13892) += mc13892.o +obj-$(CONFIG_I2C_MC34704) += mc34704.o +obj-$(CONFIG_I2C_MC9SDZ60) += mc9sdz60.o +obj-$(CONFIG_I2C_LP3972) += lp3972.o +obj-$(CONFIG_I2C_TWL4030) += twl4030.o +obj-$(CONFIG_DRIVER_SPI_MC13783) += mc13783.o diff --git a/drivers/mfd/lp3972.c b/drivers/mfd/lp3972.c new file mode 100644 index 0000000000..98266990dc --- /dev/null +++ b/drivers/mfd/lp3972.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2007 Sascha Hauer, Pengutronix + * 2009 Marc Kleine-Budde <mkl@pengutronix.de> + * 2009 Eric Benard <eric@eukrea.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#include <common.h> +#include <init.h> +#include <driver.h> +#include <xfuncs.h> +#include <errno.h> + +#include <i2c/i2c.h> + +#include <asm/byteorder.h> + +#define DRIVERNAME "lp3972" + +struct lp_priv { + struct cdev cdev; + struct i2c_client *client; +}; + +#define to_lp_priv(a) container_of(a, struct lp_priv, cdev) + +static struct lp_priv *lp_dev; + +struct i2c_client *lp3972_get_client(void) +{ + if (!lp_dev) + return NULL; + + return lp_dev->client; +} + +static u32 lp_read_reg(struct lp_priv *lp, int reg) +{ + u8 buf; + + i2c_read_reg(lp->client, reg, &buf, sizeof(buf)); + + return buf; +} + +static ssize_t lp_read(struct cdev *cdev, void *_buf, size_t count, ulong offset, ulong flags) +{ + struct lp_priv *priv = to_lp_priv(cdev); + int i = count; + u8 *buf = _buf; + + while (i) { + *buf = lp_read_reg(priv, offset); + buf++; + i--; + offset++; + } + + return count; +} + +static struct file_operations lp_fops = { + .lseek = dev_lseek_default, + .read = lp_read, +}; + +static int lp_probe(struct device_d *dev) +{ + if (lp_dev) + return -EBUSY; + + lp_dev = xzalloc(sizeof(struct lp_priv)); + lp_dev->cdev.name = DRIVERNAME; + lp_dev->client = to_i2c_client(dev); + lp_dev->cdev.size = 256; + lp_dev->cdev.dev = dev; + lp_dev->cdev.ops = &lp_fops; + + devfs_create(&lp_dev->cdev); + + return 0; +} + +static struct driver_d lp_driver = { + .name = DRIVERNAME, + .probe = lp_probe, +}; + +static int lp_init(void) +{ + register_driver(&lp_driver); + return 0; +} + +device_initcall(lp_init); diff --git a/drivers/mfd/mc13783.c b/drivers/mfd/mc13783.c new file mode 100644 index 0000000000..19e2780920 --- /dev/null +++ b/drivers/mfd/mc13783.c @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2007 Sascha Hauer, Pengutronix + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#include <common.h> +#include <init.h> +#include <driver.h> +#include <spi/spi.h> +#include <xfuncs.h> +#include <errno.h> +#include <mach/pmic.h> + +#define REG_INTERRUPT_STATUS_0 0x0 +#define REG_INTERRUPT_MASK 0x1 +#define REG_INTERRUPT_SENSE_0 0x2 +#define REG_INTERRUPT_STATUS_1 0x3 +#define REG_INTERRUPT_MASK_1 0x4 +#define REG_INTERRUPT_SENSE_1 0x5 +#define REG_POWER_UP_MODE_SENSE 0x6 +#define REG_REVISION 0x7 +#define REG_SEMAPHORE 0x8 +#define REG_ARBITRATION_PERIPHERAL_AUDIO 0x9 +#define REG_ARBITRATION_SWITCHERS 0xa +#define REG_ARBITRATION_REGULATORS(x) (0xb + (x)) /* 0 .. 1 */ +#define REG_POWER_CONTROL(x) (0xd + (x)) /* 0 .. 2 */ +#define REG_REGEN_ASSIGNMENT 0x10 +#define REG_CONTROL_SPARE 0x11 +#define REG_MEMORY_A 0x12 +#define REG_MEMORY_B 0x13 +#define REG_RTC_TIME 0x14 +#define REG_RTC_ALARM 0x15 +#define REG_RTC_DAY 0x16 +#define REG_RTC_DAY_ALARM 0x17 +#define REG_SWITCHERS(x) (0x18 + (x)) /* 0 .. 5 */ +#define REG_REGULATOR_SETTING(x) (0x1e + (x)) /* 0 .. 1 */ +#define REG_REGULATOR_MODE(x) (0x20 + (x)) /* 0 .. 1 */ +#define REG_POWER_MISCELLANEOUS 0x22 +#define REG_POWER_SPARE 0x23 +#define REG_AUDIO_RX_0 0x24 +#define REG_AUDIO_RX_1 0x25 +#define REG_AUDIO_TX 0x26 +#define REG_AUDIO_SSI_NETWORK 0x27 +#define REG_AUDIO_CODEC 0x28 +#define REG_AUDIO_STEREO_DAC 0x29 +#define REG_AUDIO_SPARE 0x2a +#define REG_ADC(x) (0x2b + (x)) /* 0 .. 4 */ +#define REG_CHARGER 0x30 +#define REG_USB 0x31 +#define REG_CHARGE_USB_SPARE 0x32 +#define REG_LED_CONTROL(x) (0x33 + (x)) /* 0 .. 5 */ +#define REG_SPARE 0x39 +#define REG_TRIM(x) (0x3a + (x)) /* 0 .. 1 */ +#define REG_TEST(x) (0x3c + (x)) /* 0 .. 3 */ + +#define MXC_PMIC_REG_NUM(reg) (((reg) & 0x3f) << 25) +#define MXC_PMIC_WRITE (1 << 31) + +#define SWX_VOLTAGE(x) ((x) & 0x3f) +#define SWX_VOLTAGE_DVS(x) (((x) & 0x3f) << 6) +#define SWX_VOLTAGE_STANDBY(x) (((x) & 0x3f) << 12) +#define SWX_VOLTAGE_1_450 0x16 + +#define SWX_MODE_OFF 0 +#define SWX_MODE_NO_PULSE_SKIP 1 +#define SWX_MODE_PULSE_SKIP 2 +#define SWX_MODE_LOW_POWER_PFM 3 + +#define SW1A_MODE(x) (((x) & 0x3) << 0) +#define SW1A_MODE_STANDBY(x) (((x) & 0x3) << 2) +#define SW1B_MODE(x) (((x) & 0x3) << 10) +#define SW1B_MODE_STANDBY(x) (((x) & 0x3) << 12) +#define SW1A_SOFTSTART (1 << 9) +#define SW1B_SOFTSTART (1 << 17) +#define SW_PLL_FACTOR(x) (((x) - 28) << 19) + +struct pmic_priv { + struct cdev cdev; + struct spi_device *spi; +}; + +static int spi_rw(struct spi_device *spi, void * buf, size_t len) +{ + int ret; + + struct spi_transfer t = { + .tx_buf = (const void *)buf, + .rx_buf = buf, + .len = len, + .cs_change = 0, + .delay_usecs = 0, + }; + struct spi_message m; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + if ((ret = spi_sync(spi, &m))) + return ret; + return 0; +} + +static uint32_t pmic_read_reg(struct pmic_priv *pmic, int reg) +{ + uint32_t buf; + + buf = MXC_PMIC_REG_NUM(reg); + + spi_rw(pmic->spi, &buf, 4); + + return buf; +} + +static void pmic_write_reg(struct pmic_priv *pmic, int reg, uint32_t val) +{ + uint32_t buf = MXC_PMIC_REG_NUM(reg) | MXC_PMIC_WRITE | (val & 0xffffff); + + spi_rw(pmic->spi, &buf, 4); +} + +static struct pmic_priv *pmic_device; + +int pmic_power(void) +{ + if(!pmic_device) { + printf("%s: no pmic device available\n", __FUNCTION__); + return -ENODEV; + } + + pmic_write_reg(pmic_device, REG_SWITCHERS(0), + SWX_VOLTAGE(SWX_VOLTAGE_1_450) | + SWX_VOLTAGE_DVS(SWX_VOLTAGE_1_450) | + SWX_VOLTAGE_STANDBY(SWX_VOLTAGE_1_450)); + + pmic_write_reg(pmic_device, REG_SWITCHERS(4), + SW1A_MODE(SWX_MODE_NO_PULSE_SKIP) | + SW1A_MODE_STANDBY(SWX_MODE_NO_PULSE_SKIP)| + SW1A_SOFTSTART | + SW1B_MODE(SWX_MODE_NO_PULSE_SKIP) | + SW1B_MODE_STANDBY(SWX_MODE_NO_PULSE_SKIP) | + SW1B_SOFTSTART | + SW_PLL_FACTOR(32) + ); + + return 0; +} + +ssize_t pmic_read(struct cdev *cdev, void *_buf, size_t count, ulong offset, ulong flags) +{ + int i = count >> 2; + uint32_t *buf = _buf; + + offset >>= 2; + + while (i) { + *buf = pmic_read_reg(pmic_device, offset); + buf++; + i--; + offset++; + } + + return count; +} + +ssize_t pmic_write(struct cdev *cdev, const void *_buf, size_t count, ulong offset, ulong flags) +{ + int i = count >> 2; + const uint32_t *buf = _buf; + + offset >>= 2; + + while (i) { + pmic_write_reg(pmic_device, offset, *buf); + buf++; + i--; + offset++; + } + + return count; +} + +static struct file_operations pmic_fops = { + .lseek = dev_lseek_default, + .read = pmic_read, + .write = pmic_write, +}; + +static int pmic_probe(struct device_d *dev) +{ + struct spi_device *spi = (struct spi_device *)dev->type_data; + + if (pmic_device) + return -EBUSY; + + pmic_device = xzalloc(sizeof(*pmic_device)); + + pmic_device->cdev.name = "pmic"; + pmic_device->cdev.size = 256; + pmic_device->cdev.dev = dev; + pmic_device->cdev.ops = &pmic_fops; + + spi->mode = SPI_MODE_0 | SPI_CS_HIGH; + spi->bits_per_word = 32; + pmic_device->spi = spi; + + devfs_create(&pmic_device->cdev); + + return 0; +} + +static struct driver_d pmic_driver = { + .name = "mc13783", + .probe = pmic_probe, +}; + +static int pmic_init(void) +{ + register_driver(&pmic_driver); + return 0; +} + +device_initcall(pmic_init); + diff --git a/drivers/mfd/mc13892.c b/drivers/mfd/mc13892.c new file mode 100644 index 0000000000..67d4232a23 --- /dev/null +++ b/drivers/mfd/mc13892.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2007 Sascha Hauer, Pengutronix + * 2009 Marc Kleine-Budde <mkl@pengutronix.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#include <common.h> +#include <init.h> +#include <driver.h> +#include <xfuncs.h> +#include <errno.h> + +#include <i2c/i2c.h> +#include <i2c/mc13892.h> + +#define DRIVERNAME "mc13892" + +#define to_mc13892(a) container_of(a, struct mc13892, cdev) + +static struct mc13892 *mc_dev; + +struct mc13892 *mc13892_get(void) +{ + if (!mc_dev) + return NULL; + + return mc_dev; +} +EXPORT_SYMBOL(mc13892_get); + +int mc13892_reg_read(struct mc13892 *mc13892, enum mc13892_reg reg, u32 *val) +{ + u8 buf[3]; + int ret; + + ret = i2c_read_reg(mc13892->client, reg, buf, 3); + *val = buf[0] << 16 | buf[1] << 8 | buf[2] << 0; + + return ret == 3 ? 0 : ret; +} +EXPORT_SYMBOL(mc13892_reg_read) + +int mc13892_reg_write(struct mc13892 *mc13892, enum mc13892_reg reg, u32 val) +{ + u8 buf[] = { + val >> 16, + val >> 8, + val >> 0, + }; + int ret; + + ret = i2c_write_reg(mc13892->client, reg, buf, 3); + + return ret == 3 ? 0 : ret; +} +EXPORT_SYMBOL(mc13892_reg_write) + +int mc13892_set_bits(struct mc13892 *mc13892, enum mc13892_reg reg, u32 mask, u32 val) +{ + u32 tmp; + int err; + + err = mc13892_reg_read(mc13892, reg, &tmp); + tmp = (tmp & ~mask) | val; + + if (!err) + err = mc13892_reg_write(mc13892, reg, tmp); + + return err; +} +EXPORT_SYMBOL(mc13892_set_bits); + +static ssize_t mc_read(struct cdev *cdev, void *_buf, size_t count, ulong offset, ulong flags) +{ + struct mc13892 *priv = to_mc13892(cdev); + u32 *buf = _buf; + size_t i = count >> 2; + int err; + + offset >>= 2; + + while (i) { + err = mc13892_reg_read(priv, offset, buf); + if (err) + return (ssize_t)err; + buf++; + i--; + offset++; + } + + return count; +} + +static ssize_t mc_write(struct cdev *cdev, const void *_buf, size_t count, ulong offset, ulong flags) +{ + struct mc13892 *mc13892 = to_mc13892(cdev); + const u32 *buf = _buf; + size_t i = count >> 2; + int err; + + offset >>= 2; + + while (i) { + err = mc13892_reg_write(mc13892, offset, *buf); + if (err) + return (ssize_t)err; + buf++; + i--; + offset++; + } + + return count; +} + +static struct file_operations mc_fops = { + .lseek = dev_lseek_default, + .read = mc_read, + .write = mc_write, +}; + +static int mc_probe(struct device_d *dev) +{ + if (mc_dev) + return -EBUSY; + + mc_dev = xzalloc(sizeof(struct mc13892)); + mc_dev->cdev.name = DRIVERNAME; + mc_dev->client = to_i2c_client(dev); + mc_dev->cdev.size = 256; + mc_dev->cdev.dev = dev; + mc_dev->cdev.ops = &mc_fops; + + devfs_create(&mc_dev->cdev); + + return 0; +} + +static struct driver_d mc_driver = { + .name = DRIVERNAME, + .probe = mc_probe, +}; + +static int mc_init(void) +{ + register_driver(&mc_driver); + return 0; +} + +device_initcall(mc_init); diff --git a/drivers/mfd/mc34704.c b/drivers/mfd/mc34704.c new file mode 100644 index 0000000000..51a8737209 --- /dev/null +++ b/drivers/mfd/mc34704.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2007 Sascha Hauer, Pengutronix + * 2009 Marc Kleine-Budde <mkl@pengutronix.de> + * Copyright (C) 2010 Baruch Siach <baruch@tkos.co.il> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#include <common.h> +#include <init.h> +#include <driver.h> +#include <xfuncs.h> +#include <errno.h> + +#include <i2c/i2c.h> +#include <i2c/mc34704.h> + +#define DRIVERNAME "mc34704" + +#define to_mc34704(a) container_of(a, struct mc34704, cdev) + +static struct mc34704 *mc34704_dev; + +struct mc34704 *mc34704_get(void) +{ + if (!mc34704_dev) + return NULL; + + return mc34704_dev; +} +EXPORT_SYMBOL(mc34704_get); + +int mc34704_reg_read(struct mc34704 *mc34704, u8 reg, u8 *val) +{ + int ret; + + ret = i2c_read_reg(mc34704->client, reg, val, 1); + + return ret == 1 ? 0 : ret; +} +EXPORT_SYMBOL(mc34704_reg_read) + +int mc34704_reg_write(struct mc34704 *mc34704, u8 reg, u8 val) +{ + int ret; + + ret = i2c_write_reg(mc34704->client, reg, &val, 1); + + return ret == 1 ? 0 : ret; +} +EXPORT_SYMBOL(mc34704_reg_write) + +static ssize_t mc34704_read(struct cdev *cdev, void *_buf, size_t count, + ulong offset, ulong flags) +{ + struct mc34704 *priv = to_mc34704(cdev); + u8 *buf = _buf; + size_t i = count; + int err; + + while (i) { + err = mc34704_reg_read(priv, offset, buf); + if (err) + return (ssize_t)err; + buf++; + i--; + offset++; + } + + return count; +} + +static ssize_t mc34704_write(struct cdev *cdev, const void *_buf, size_t count, + ulong offset, ulong flags) +{ + struct mc34704 *mc34704 = to_mc34704(cdev); + const u8 *buf = _buf; + size_t i = count; + int err; + + while (i) { + err = mc34704_reg_write(mc34704, offset, *buf); + if (err) + return (ssize_t)err; + buf++; + i--; + offset++; + } + + return count; +} + +static struct file_operations mc34704_fops = { + .lseek = dev_lseek_default, + .read = mc34704_read, + .write = mc34704_write, +}; + +static int mc34704_probe(struct device_d *dev) +{ + if (mc34704_dev) + return -EBUSY; + + mc34704_dev = xzalloc(sizeof(struct mc34704)); + mc34704_dev->cdev.name = DRIVERNAME; + mc34704_dev->client = to_i2c_client(dev); + mc34704_dev->cdev.size = 256; + mc34704_dev->cdev.dev = dev; + mc34704_dev->cdev.ops = &mc34704_fops; + + devfs_create(&mc34704_dev->cdev); + + return 0; +} + +static struct driver_d mc34704_driver = { + .name = DRIVERNAME, + .probe = mc34704_probe, +}; + +static int mc34704_init(void) +{ + register_driver(&mc34704_driver); + return 0; +} +device_initcall(mc34704_init); diff --git a/drivers/mfd/mc9sdz60.c b/drivers/mfd/mc9sdz60.c new file mode 100644 index 0000000000..3580af8852 --- /dev/null +++ b/drivers/mfd/mc9sdz60.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2007 Sascha Hauer, Pengutronix + * 2009 Marc Kleine-Budde <mkl@pengutronix.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#include <common.h> +#include <init.h> +#include <driver.h> +#include <xfuncs.h> +#include <errno.h> + +#include <i2c/i2c.h> +#include <i2c/mc9sdz60.h> + +#define DRIVERNAME "mc9sdz60" + +#define to_mc9sdz60(a) container_of(a, struct mc9sdz60, cdev) + +static struct mc9sdz60 *mc_dev; + +struct mc9sdz60 *mc9sdz60_get(void) +{ + if (!mc_dev) + return NULL; + + return mc_dev; +} +EXPORT_SYMBOL(mc9sdz60_get); + +int mc9sdz60_reg_read(struct mc9sdz60 *mc9sdz60, enum mc9sdz60_reg reg, u8 *val) +{ + int ret; + + ret = i2c_read_reg(mc9sdz60->client, reg, val, 1); + + return ret == 1 ? 0 : ret; +} +EXPORT_SYMBOL(mc9sdz60_reg_read) + +int mc9sdz60_reg_write(struct mc9sdz60 *mc9sdz60, enum mc9sdz60_reg reg, u8 val) +{ + int ret; + + ret = i2c_write_reg(mc9sdz60->client, reg, &val, 1); + + return ret == 1 ? 0 : ret; +} +EXPORT_SYMBOL(mc9sdz60_reg_write) + +int mc9sdz60_set_bits(struct mc9sdz60 *mc9sdz60, enum mc9sdz60_reg reg, u8 mask, u8 val) +{ + u8 tmp; + int err; + + err = mc9sdz60_reg_read(mc9sdz60, reg, &tmp); + tmp = (tmp & ~mask) | val; + + if (!err) + err = mc9sdz60_reg_write(mc9sdz60, reg, tmp); + + return err; +} +EXPORT_SYMBOL(mc9sdz60_set_bits); + +static ssize_t mc_read(struct cdev *cdev, void *_buf, size_t count, ulong offset, ulong flags) +{ + struct mc9sdz60 *mc9sdz60 = to_mc9sdz60(cdev); + u8 *buf = _buf; + size_t i = count; + int err; + + while (i) { + err = mc9sdz60_reg_read(mc9sdz60, offset, buf); + if (err) + return (ssize_t)err; + buf++; + i--; + offset++; + } + + return count; +} + +static ssize_t mc_write(struct cdev *cdev, const void *_buf, size_t count, ulong offset, ulong flags) +{ + struct mc9sdz60 *mc9sdz60 = to_mc9sdz60(cdev); + const u8 *buf = _buf; + size_t i = count; + int err; + + while (i) { + err = mc9sdz60_reg_write(mc9sdz60, offset, *buf); + if (err) + return (ssize_t)err; + buf++; + i--; + offset++; + } + + return count; +} + +static struct file_operations mc_fops = { + .lseek = dev_lseek_default, + .read = mc_read, + .write = mc_write, +}; + +static int mc_probe(struct device_d *dev) +{ + if (mc_dev) + return -EBUSY; + + mc_dev = xzalloc(sizeof(struct mc9sdz60)); + mc_dev->cdev.name = DRIVERNAME; + mc_dev->client = to_i2c_client(dev); + mc_dev->cdev.size = 64; /* 35 known registers */ + mc_dev->cdev.dev = dev; + mc_dev->cdev.ops = &mc_fops; + + devfs_create(&mc_dev->cdev); + + return 0; +} + +static struct driver_d mc_driver = { + .name = DRIVERNAME, + .probe = mc_probe, +}; + +static int mc_init(void) +{ + register_driver(&mc_driver); + return 0; +} + +device_initcall(mc_init); diff --git a/drivers/mfd/twl4030.c b/drivers/mfd/twl4030.c new file mode 100644 index 0000000000..5305ec67e3 --- /dev/null +++ b/drivers/mfd/twl4030.c @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2010 Michael Grzeschik <mgr@pengutronix.de> + * + * This file is released under the GPLv2 + * + */ + +#include <common.h> +#include <init.h> +#include <driver.h> +#include <xfuncs.h> +#include <errno.h> + +#include <i2c/i2c.h> +#include <i2c/twl4030.h> + +#define DRIVERNAME "twl4030" + +#define to_twl4030(a) container_of(a, struct twl4030, cdev) + +static struct twl4030 *twl_dev; + +struct twl4030 *twl4030_get(void) +{ + if (!twl_dev) + return NULL; + + return twl_dev; +} +EXPORT_SYMBOL(twl4030_get); + +int twl4030_reg_read(struct twl4030 *twl4030, u16 reg, u8 *val) +{ + int ret; + struct i2c_msg xfer_msg[2]; + struct i2c_msg *msg; + int i2c_addr; + unsigned char buf = reg & 0xff; + + i2c_addr = twl4030->client->addr + (reg / 0x100); + + /* [MSG1] fill the register address data */ + msg = &xfer_msg[0]; + msg->addr = i2c_addr; + msg->len = 1; + msg->flags = 0; /* Read the register value */ + msg->buf = &buf; + /* [MSG2] fill the data rx buffer */ + msg = &xfer_msg[1]; + msg->addr = i2c_addr; + msg->flags = I2C_M_RD; /* Read the register value */ + msg->len = 1; /* only n bytes */ + msg->buf = val; + ret = i2c_transfer(twl4030->client->adapter, xfer_msg, 2); + + /* i2c_transfer returns number of messages transferred */ + if (ret < 0) { + pr_err("%s: failed to transfer all messages: %s\n", __func__, strerror(-ret)); + return ret; + } + return 0; +} +EXPORT_SYMBOL(twl4030_reg_read) + +int twl4030_reg_write(struct twl4030 *twl4030, u16 reg, u8 val) +{ + int ret; + struct i2c_msg xfer_msg[1]; + struct i2c_msg *msg; + int i2c_addr; + u8 buf[2]; + + buf[0] = reg & 0xff; + buf[1] = val; + + i2c_addr = twl4030->client->addr + (reg / 0x100); + + /* + * [MSG1]: fill the register address data + * fill the data Tx buffer + */ + msg = xfer_msg; + msg->addr = i2c_addr; + msg->len = 2; + msg->flags = 0; + msg->buf = buf; + /* over write the first byte of buffer with the register address */ + ret = i2c_transfer(twl4030->client->adapter, xfer_msg, 1); + + /* i2c_transfer returns number of messages transferred */ + if (ret < 0) { + pr_err("%s: failed to transfer all messages: %s\n", __func__, strerror(-ret)); + return ret; + } + return 0; +} +EXPORT_SYMBOL(twl4030_reg_write) + +int twl4030_set_bits(struct twl4030 *twl4030, enum twl4030_reg reg, u8 mask, u8 val) +{ + u8 tmp; + int err; + + err = twl4030_reg_read(twl4030, reg, &tmp); + tmp = (tmp & ~mask) | val; + + if (!err) + err = twl4030_reg_write(twl4030, reg, tmp); + + return err; +} +EXPORT_SYMBOL(twl4030_set_bits); + +static ssize_t twl_read(struct cdev *cdev, void *_buf, size_t count, ulong offset, ulong flags) +{ + struct twl4030 *priv = to_twl4030(cdev); + u8 *buf = _buf; + size_t i = count; + int err; + + while (i) { + err = twl4030_reg_read(priv, offset, buf); + if (err) + return (ssize_t)err; + buf++; + i--; + offset++; + } + + return count; +} + +static ssize_t twl_write(struct cdev *cdev, const void *_buf, size_t count, ulong offset, ulong flags) +{ + struct twl4030 *twl4030 = to_twl4030(cdev); + const u8 *buf = _buf; + size_t i = count; + int err; + + while (i) { + err = twl4030_reg_write(twl4030, offset, *buf); + if (err) + return (ssize_t)err; + buf++; + i--; + offset++; + } + + return count; +} + +static struct file_operations twl_fops = { + .lseek = dev_lseek_default, + .read = twl_read, + .write = twl_write, +}; + +static int twl_probe(struct device_d *dev) +{ + if (twl_dev) + return -EBUSY; + + twl_dev = xzalloc(sizeof(struct twl4030)); + twl_dev->cdev.name = DRIVERNAME; + twl_dev->client = to_i2c_client(dev); + twl_dev->cdev.size = 1024; + twl_dev->cdev.dev = dev; + twl_dev->cdev.ops = &twl_fops; + + devfs_create(&twl_dev->cdev); + + return 0; +} + +static struct driver_d twl_driver = { + .name = DRIVERNAME, + .probe = twl_probe, +}; + +static int twl_init(void) +{ + register_driver(&twl_driver); + return 0; +} + +device_initcall(twl_init); |