Skip to content

Commit

Permalink
rtc-v3020: add ability to access v3020 chip with GPIOs
Browse files Browse the repository at this point in the history
The v3020 RTC can be connected to GPIOs as well as to memory-like
interface.  Add ability to use GPIO bit-bang for v3020 read-write access.

[akpm@linux-foundation.org: fix off-by-one in error path]
Signed-off-by: Mike Rapoport <mike@compulab.co.il>
Acked-by: Alessandro Zummo <a.zummo@towertech.it>
Cc: David Brownell <david-b@pacbell.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Mike Rapoport authored and Linus Torvalds committed Apr 3, 2009
1 parent c1c490e commit 9661584
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 20 deletions.
190 changes: 170 additions & 20 deletions drivers/rtc/rtc-v3020.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,162 @@
#include <linux/bcd.h>
#include <linux/rtc-v3020.h>
#include <linux/delay.h>
#include <linux/gpio.h>

#include <linux/io.h>

#undef DEBUG

struct v3020;

struct v3020_chip_ops {
int (*map_io)(struct v3020 *chip, struct platform_device *pdev,
struct v3020_platform_data *pdata);
void (*unmap_io)(struct v3020 *chip);
unsigned char (*read_bit)(struct v3020 *chip);
void (*write_bit)(struct v3020 *chip, unsigned char bit);
};

#define V3020_CS 0
#define V3020_WR 1
#define V3020_RD 2
#define V3020_IO 3

struct v3020_gpio {
const char *name;
unsigned int gpio;
};

struct v3020 {
/* MMIO access */
void __iomem *ioaddress;
int leftshift;

/* GPIO access */
struct v3020_gpio *gpio;

struct v3020_chip_ops *ops;

struct rtc_device *rtc;
};


static int v3020_mmio_map(struct v3020 *chip, struct platform_device *pdev,
struct v3020_platform_data *pdata)
{
if (pdev->num_resources != 1)
return -EBUSY;

if (pdev->resource[0].flags != IORESOURCE_MEM)
return -EBUSY;

chip->leftshift = pdata->leftshift;
chip->ioaddress = ioremap(pdev->resource[0].start, 1);
if (chip->ioaddress == NULL)
return -EBUSY;

return 0;
}

static void v3020_mmio_unmap(struct v3020 *chip)
{
iounmap(chip->ioaddress);
}

static void v3020_mmio_write_bit(struct v3020 *chip, unsigned char bit)
{
writel(bit << chip->leftshift, chip->ioaddress);
}

static unsigned char v3020_mmio_read_bit(struct v3020 *chip)
{
return readl(chip->ioaddress) & (1 << chip->leftshift);
}

static struct v3020_chip_ops v3020_mmio_ops = {
.map_io = v3020_mmio_map,
.unmap_io = v3020_mmio_unmap,
.read_bit = v3020_mmio_read_bit,
.write_bit = v3020_mmio_write_bit,
};

static struct v3020_gpio v3020_gpio[] = {
{ "RTC CS", 0 },
{ "RTC WR", 0 },
{ "RTC RD", 0 },
{ "RTC IO", 0 },
};

static int v3020_gpio_map(struct v3020 *chip, struct platform_device *pdev,
struct v3020_platform_data *pdata)
{
int i, err;

v3020_gpio[V3020_CS].gpio = pdata->gpio_cs;
v3020_gpio[V3020_WR].gpio = pdata->gpio_wr;
v3020_gpio[V3020_RD].gpio = pdata->gpio_rd;
v3020_gpio[V3020_IO].gpio = pdata->gpio_io;

for (i = 0; i < ARRAY_SIZE(v3020_gpio); i++) {
err = gpio_request(v3020_gpio[i].gpio, v3020_gpio[i].name);
if (err)
goto err_request;

gpio_direction_output(v3020_gpio[i].gpio, 1);
}

chip->gpio = v3020_gpio;

return 0;

err_request:
while (--i >= 0)
gpio_free(v3020_gpio[i].gpio);

return err;
}

static void v3020_gpio_unmap(struct v3020 *chip)
{
int i;

for (i = 0; i < ARRAY_SIZE(v3020_gpio); i++)
gpio_free(v3020_gpio[i].gpio);
}

static void v3020_gpio_write_bit(struct v3020 *chip, unsigned char bit)
{
gpio_direction_output(chip->gpio[V3020_IO].gpio, bit);
gpio_set_value(chip->gpio[V3020_CS].gpio, 0);
gpio_set_value(chip->gpio[V3020_WR].gpio, 0);
udelay(1);
gpio_set_value(chip->gpio[V3020_WR].gpio, 1);
gpio_set_value(chip->gpio[V3020_CS].gpio, 1);
}

static unsigned char v3020_gpio_read_bit(struct v3020 *chip)
{
int bit;

gpio_direction_input(chip->gpio[V3020_IO].gpio);
gpio_set_value(chip->gpio[V3020_CS].gpio, 0);
gpio_set_value(chip->gpio[V3020_RD].gpio, 0);
udelay(1);
bit = !!gpio_get_value(chip->gpio[V3020_IO].gpio);
udelay(1);
gpio_set_value(chip->gpio[V3020_RD].gpio, 1);
gpio_set_value(chip->gpio[V3020_CS].gpio, 1);

return bit;
}

static struct v3020_chip_ops v3020_gpio_ops = {
.map_io = v3020_gpio_map,
.unmap_io = v3020_gpio_unmap,
.read_bit = v3020_gpio_read_bit,
.write_bit = v3020_gpio_write_bit,
};

static void v3020_set_reg(struct v3020 *chip, unsigned char address,
unsigned char data)
{
Expand All @@ -46,15 +191,15 @@ static void v3020_set_reg(struct v3020 *chip, unsigned char address,

tmp = address;
for (i = 0; i < 4; i++) {
writel((tmp & 1) << chip->leftshift, chip->ioaddress);
chip->ops->write_bit(chip, (tmp & 1));
tmp >>= 1;
udelay(1);
}

/* Commands dont have data */
if (!V3020_IS_COMMAND(address)) {
for (i = 0; i < 8; i++) {
writel((data & 1) << chip->leftshift, chip->ioaddress);
chip->ops->write_bit(chip, (data & 1));
data >>= 1;
udelay(1);
}
Expand All @@ -67,14 +212,14 @@ static unsigned char v3020_get_reg(struct v3020 *chip, unsigned char address)
int i;

for (i = 0; i < 4; i++) {
writel((address & 1) << chip->leftshift, chip->ioaddress);
chip->ops->write_bit(chip, (address & 1));
address >>= 1;
udelay(1);
}

for (i = 0; i < 8; i++) {
data >>= 1;
if (readl(chip->ioaddress) & (1 << chip->leftshift))
if (chip->ops->read_bit(chip))
data |= 0x80;
udelay(1);
}
Expand Down Expand Up @@ -164,25 +309,23 @@ static int rtc_probe(struct platform_device *pdev)
int i;
int temp;

if (pdev->num_resources != 1)
return -EBUSY;

if (pdev->resource[0].flags != IORESOURCE_MEM)
return -EBUSY;

chip = kzalloc(sizeof *chip, GFP_KERNEL);
if (!chip)
return -ENOMEM;

chip->leftshift = pdata->leftshift;
chip->ioaddress = ioremap(pdev->resource[0].start, 1);
if (chip->ioaddress == NULL)
if (pdata->use_gpio)
chip->ops = &v3020_gpio_ops;
else
chip->ops = &v3020_mmio_ops;

retval = chip->ops->map_io(chip, pdev, pdata);
if (retval)
goto err_chip;

/* Make sure the v3020 expects a communication cycle
* by reading 8 times */
for (i = 0; i < 8; i++)
temp = readl(chip->ioaddress);
temp = chip->ops->read_bit(chip);

/* Test chip by doing a write/read sequence
* to the chip ram */
Expand All @@ -196,10 +339,17 @@ static int rtc_probe(struct platform_device *pdev)
* are all disabled */
v3020_set_reg(chip, V3020_STATUS_0, 0x0);

dev_info(&pdev->dev, "Chip available at physical address 0x%llx,"
"data connected to D%d\n",
(unsigned long long)pdev->resource[0].start,
chip->leftshift);
if (pdata->use_gpio)
dev_info(&pdev->dev, "Chip available at GPIOs "
"%d, %d, %d, %d\n",
chip->gpio[V3020_CS].gpio, chip->gpio[V3020_WR].gpio,
chip->gpio[V3020_RD].gpio, chip->gpio[V3020_IO].gpio);
else
dev_info(&pdev->dev, "Chip available at "
"physical address 0x%llx,"
"data connected to D%d\n",
(unsigned long long)pdev->resource[0].start,
chip->leftshift);

platform_set_drvdata(pdev, chip);

Expand All @@ -214,7 +364,7 @@ static int rtc_probe(struct platform_device *pdev)
return 0;

err_io:
iounmap(chip->ioaddress);
chip->ops->unmap_io(chip);
err_chip:
kfree(chip);

Expand All @@ -229,7 +379,7 @@ static int rtc_remove(struct platform_device *dev)
if (rtc)
rtc_device_unregister(rtc);

iounmap(chip->ioaddress);
chip->ops->unmap_io(chip);
kfree(chip);

return 0;
Expand Down
6 changes: 6 additions & 0 deletions include/linux/rtc-v3020.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
* is used depends on the board. */
struct v3020_platform_data {
int leftshift; /* (1<<(leftshift)) & readl() */

int use_gpio:1;
unsigned int gpio_cs;
unsigned int gpio_wr;
unsigned int gpio_rd;
unsigned int gpio_io;
};

#define V3020_STATUS_0 0x00
Expand Down

0 comments on commit 9661584

Please sign in to comment.