178 lines
3.9 KiB
C
178 lines
3.9 KiB
C
/*
|
|
* Copyright (C) 2012 Jan Luebbe, 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 <errno.h>
|
|
|
|
#define REG_ID0 0x00
|
|
#define REG_ID1 0x01
|
|
|
|
#define REG_GC00 0x02
|
|
#define REG_GC01 0x03
|
|
#define REG_GC02 0x04
|
|
#define REG_GC03 0x05
|
|
#define REG_GC04 0x06
|
|
#define REG_GC05 0x07
|
|
#define REG_GC06 0x08
|
|
#define REG_GC07 0x09
|
|
#define REG_GC08 0x0a
|
|
#define REG_GC09 0x0b
|
|
#define REG_GC10 0x0c
|
|
#define REG_GC11 0x0d
|
|
|
|
#define REG_PSTAT1(p) (0x10 * p + 0xe)
|
|
#define REG_PSTAT2(p) (0x10 * p + 0xf)
|
|
|
|
#define CMD_WRITE 0x02
|
|
#define CMD_READ 0x03
|
|
|
|
struct micrel_switch_priv {
|
|
struct cdev cdev;
|
|
struct spi_device *spi;
|
|
unsigned int p_enable;
|
|
};
|
|
|
|
static int micrel_switch_read_reg(struct spi_device *spi, uint8_t reg)
|
|
{
|
|
uint8_t tx[2];
|
|
uint8_t rx[1];
|
|
int ret;
|
|
|
|
tx[0] = CMD_READ;
|
|
tx[1] = reg;
|
|
|
|
ret = spi_write_then_read(spi, tx, 2, rx, 1);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return rx[0];
|
|
}
|
|
|
|
static void micrel_switch_write_reg(struct spi_device *spi, uint8_t reg, uint8_t val)
|
|
{
|
|
uint8_t tx[3];
|
|
|
|
tx[0] = CMD_WRITE;
|
|
tx[1] = reg;
|
|
tx[2] = val;
|
|
|
|
spi_write_then_read(spi, tx, 3, NULL, 0);
|
|
}
|
|
|
|
static int micrel_switch_enable_set(struct param_d *param, void *_priv)
|
|
{
|
|
struct micrel_switch_priv *priv = _priv;
|
|
struct spi_device *spi = priv->spi;
|
|
|
|
if (priv->p_enable)
|
|
micrel_switch_write_reg(spi, REG_ID1, 1);
|
|
else
|
|
micrel_switch_write_reg(spi, REG_ID1, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t micel_switch_read(struct cdev *cdev, void *_buf, size_t count, loff_t offset, ulong flags)
|
|
{
|
|
int i, ret;
|
|
uint8_t *buf = _buf;
|
|
struct micrel_switch_priv *priv = cdev->priv;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
ret = micrel_switch_read_reg(priv->spi, offset);
|
|
if (ret < 0)
|
|
return ret;
|
|
*buf = ret;
|
|
buf++;
|
|
offset++;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t micel_switch_write(struct cdev *cdev, const void *_buf, size_t count, loff_t offset, ulong flags)
|
|
{
|
|
int i;
|
|
const uint8_t *buf = _buf;
|
|
struct micrel_switch_priv *priv = cdev->priv;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
micrel_switch_write_reg(priv->spi, offset, *buf);
|
|
buf++;
|
|
offset++;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static struct file_operations micrel_switch_ops = {
|
|
.read = micel_switch_read,
|
|
.write = micel_switch_write,
|
|
.lseek = dev_lseek_default,
|
|
};
|
|
|
|
static int micrel_switch_probe(struct device_d *dev)
|
|
{
|
|
struct micrel_switch_priv *priv;
|
|
int ret = 0;
|
|
|
|
priv = xzalloc(sizeof(*priv));
|
|
|
|
dev->priv = priv;
|
|
|
|
priv->spi = (struct spi_device *)dev->type_data;
|
|
priv->spi->mode = SPI_MODE_0;
|
|
priv->spi->bits_per_word = 8;
|
|
|
|
ret = micrel_switch_read_reg(priv->spi, REG_ID0);
|
|
if (ret < 0) {
|
|
dev_err(&priv->spi->dev, "failed to read device id\n");
|
|
return ret;
|
|
}
|
|
if (ret != 0x95) {
|
|
dev_err(&priv->spi->dev, "unknown device id: %02x\n", ret);
|
|
return -ENODEV;
|
|
}
|
|
|
|
priv->cdev.name = asprintf("switch%d", dev->id);
|
|
priv->cdev.size = 256;
|
|
priv->cdev.ops = &micrel_switch_ops;
|
|
priv->cdev.priv = priv;
|
|
priv->cdev.dev = dev;
|
|
devfs_create(&priv->cdev);
|
|
|
|
dev_add_param_bool(dev, "enable", micrel_switch_enable_set,
|
|
NULL, &priv->p_enable, priv);
|
|
|
|
priv->p_enable = 1;
|
|
micrel_switch_write_reg(priv->spi, REG_ID1, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct driver_d micrel_switch_driver = {
|
|
.name = "ksz8864rmn",
|
|
.probe = micrel_switch_probe,
|
|
};
|
|
device_spi_driver(micrel_switch_driver);
|