Add support for the Allwinner i2c device. This is similar to the existing
Marvell twsi part, however uses different register locations, as such split the existing driver into Marvell and Allwinner attachments. While here clean a few style issues. Submitted by: Emmanuel Vadot <manu@bidouilliste.com> Differential Revision: https://reviews.freebsd.org/D4846
This commit is contained in:
parent
355920c5b7
commit
3245bc3a66
@ -400,6 +400,29 @@ a10_clk_mmc_cfg(int devid, int freq)
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
a10_clk_i2c_activate(int devid)
|
||||
{
|
||||
struct a10_ccm_softc *sc;
|
||||
uint32_t reg_value;
|
||||
|
||||
sc = a10_ccm_sc;
|
||||
if (sc == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
a10_clk_pll6_enable();
|
||||
|
||||
/* Gating APB clock for I2C/TWI */
|
||||
reg_value = ccm_read_4(sc, CCM_APB1_GATING);
|
||||
if (devid == 4)
|
||||
reg_value |= CCM_APB1_GATING_TWI << 15;
|
||||
else
|
||||
reg_value |= CCM_APB1_GATING_TWI << devid;
|
||||
ccm_write_4(sc, CCM_APB1_GATING, reg_value);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
a10_clk_dmac_activate(void)
|
||||
{
|
||||
|
@ -121,6 +121,9 @@
|
||||
/* AHB_GATING_REG1 */
|
||||
#define CCM_AHB_GATING_GMAC (1 << 17)
|
||||
|
||||
/* APB1_GATING_REG */
|
||||
#define CCM_APB1_GATING_TWI (1 << 0)
|
||||
|
||||
#define CCM_USB_PHY (1 << 8)
|
||||
#define CCM_USB0_RESET (1 << 0)
|
||||
#define CCM_USB1_RESET (1 << 1)
|
||||
@ -166,6 +169,7 @@ int a10_clk_gmac_activate(phandle_t);
|
||||
int a10_clk_ahci_activate(void);
|
||||
int a10_clk_mmc_activate(int);
|
||||
int a10_clk_mmc_cfg(int, int);
|
||||
int a10_clk_i2c_activate(int);
|
||||
int a10_clk_dmac_activate(void);
|
||||
int a10_clk_codec_activate(unsigned int);
|
||||
|
||||
|
@ -12,4 +12,5 @@ arm/allwinner/a10_wdog.c standard
|
||||
arm/allwinner/a20/a20_cpu_cfg.c standard
|
||||
arm/allwinner/allwinner_machdep.c standard
|
||||
arm/allwinner/if_emac.c optional emac
|
||||
dev/iicbus/twsi/a10_twsi.c optional twsi
|
||||
#arm/allwinner/console.c standard
|
||||
|
@ -66,8 +66,9 @@ device md
|
||||
device random # Entropy device
|
||||
|
||||
# I2C support
|
||||
#device iicbus
|
||||
#device iic
|
||||
device iicbus
|
||||
device iic
|
||||
device twsi
|
||||
|
||||
# GPIO
|
||||
device gpio
|
||||
|
@ -75,8 +75,9 @@ device md
|
||||
device random # Entropy device
|
||||
|
||||
# I2C support
|
||||
#device iicbus
|
||||
#device iic
|
||||
device iicbus
|
||||
device iic
|
||||
device twsi
|
||||
|
||||
# GPIO
|
||||
device gpio
|
||||
|
@ -21,6 +21,7 @@ arm/mv/mv_ts.c standard
|
||||
arm/mv/timer.c standard
|
||||
|
||||
dev/cesa/cesa.c optional cesa
|
||||
dev/iicbus/twsi/mv_twsi.c optional twsi
|
||||
dev/mge/if_mge.c optional mge
|
||||
dev/nand/nfc_mv.c optional nand
|
||||
dev/mvs/mvs_soc.c optional mvs
|
||||
|
131
sys/dev/iicbus/twsi/a10_twsi.c
Normal file
131
sys/dev/iicbus/twsi/a10_twsi.c
Normal file
@ -0,0 +1,131 @@
|
||||
/*-
|
||||
* Copyright (c) 2016 Emmanuel Vadot <manu@bidouilliste.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <machine/resource.h>
|
||||
|
||||
#include <sys/rman.h>
|
||||
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
|
||||
#include <dev/iicbus/iiconf.h>
|
||||
#include <dev/iicbus/iicbus.h>
|
||||
#include <dev/iicbus/twsi/twsi.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include <arm/allwinner/a10_clk.h>
|
||||
|
||||
#include "iicbus_if.h"
|
||||
|
||||
#define TWI_ADDR 0x0
|
||||
#define TWI_XADDR 0x4
|
||||
#define TWI_DATA 0x8
|
||||
#define TWI_CNTR 0xC
|
||||
#define TWI_STAT 0x10
|
||||
#define TWI_CCR 0x14
|
||||
#define TWI_SRST 0x18
|
||||
#define TWI_EFR 0x1C
|
||||
#define TWI_LCR 0x20
|
||||
|
||||
static int
|
||||
a10_twsi_probe(device_t dev)
|
||||
{
|
||||
struct twsi_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
if (!ofw_bus_status_okay(dev))
|
||||
return (ENXIO);
|
||||
|
||||
if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-i2c"))
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, "Allwinner Integrated I2C Bus Controller");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
a10_twsi_attach(device_t dev)
|
||||
{
|
||||
struct twsi_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
/* Activate clock */
|
||||
a10_clk_i2c_activate(device_get_unit(dev));
|
||||
|
||||
sc->reg_data = TWI_DATA;
|
||||
sc->reg_slave_addr = TWI_ADDR;
|
||||
sc->reg_slave_ext_addr = TWI_XADDR;
|
||||
sc->reg_control = TWI_CNTR;
|
||||
sc->reg_status = TWI_STAT;
|
||||
sc->reg_baud_rate = TWI_CCR;
|
||||
sc->reg_soft_reset = TWI_SRST;
|
||||
|
||||
/* Setup baud rate params */
|
||||
sc->baud_rate[IIC_SLOW].param = TWSI_BAUD_RATE_PARAM(11, 2);
|
||||
sc->baud_rate[IIC_FAST].param = TWSI_BAUD_RATE_PARAM(11, 2);
|
||||
sc->baud_rate[IIC_FASTEST].param = TWSI_BAUD_RATE_PARAM(2, 2);
|
||||
|
||||
return (twsi_attach(dev));
|
||||
}
|
||||
|
||||
static phandle_t
|
||||
a10_twsi_get_node(device_t bus, device_t dev)
|
||||
{
|
||||
return (ofw_bus_get_node(bus));
|
||||
}
|
||||
|
||||
static device_method_t a10_twsi_methods[] = {
|
||||
/* device interface */
|
||||
DEVMETHOD(device_probe, a10_twsi_probe),
|
||||
DEVMETHOD(device_attach, a10_twsi_attach),
|
||||
|
||||
/* OFW methods */
|
||||
DEVMETHOD(ofw_bus_get_node, a10_twsi_get_node),
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
DEFINE_CLASS_1(iichb, a10_twsi_driver, a10_twsi_methods,
|
||||
sizeof(struct twsi_softc), twsi_driver);
|
||||
|
||||
static devclass_t a10_twsi_devclass;
|
||||
|
||||
DRIVER_MODULE(a10_twsi, simplebus, a10_twsi_driver, a10_twsi_devclass, 0, 0);
|
||||
DRIVER_MODULE(iicbus, a10_twsi, iicbus_driver, iicbus_devclass, 0, 0);
|
||||
MODULE_DEPEND(a10_twsi, iicbus, 1, 1, 1);
|
252
sys/dev/iicbus/twsi/mv_twsi.c
Normal file
252
sys/dev/iicbus/twsi/mv_twsi.c
Normal file
@ -0,0 +1,252 @@
|
||||
/*-
|
||||
* Copyright (C) 2008 MARVELL INTERNATIONAL LTD.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by Semihalf.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of MARVELL nor the names of contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Driver for the TWSI (aka I2C, aka IIC) bus controller found on Marvell
|
||||
* and Allwinner SoCs. Supports master operation only, and works in polling mode.
|
||||
*
|
||||
* Calls to DELAY() are needed per Application Note AN-179 "TWSI Software
|
||||
* Guidelines for Discovery(TM), Horizon (TM) and Feroceon(TM) Devices".
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <machine/_inttypes.h>
|
||||
#include <machine/bus.h>
|
||||
#include <machine/resource.h>
|
||||
|
||||
#include <sys/rman.h>
|
||||
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
|
||||
#include <dev/iicbus/iiconf.h>
|
||||
#include <dev/iicbus/iicbus.h>
|
||||
#include <dev/fdt/fdt_common.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include <arm/mv/mvreg.h>
|
||||
#include <arm/mv/mvvar.h>
|
||||
#include <dev/iicbus/twsi/twsi.h>
|
||||
|
||||
#include "iicbus_if.h"
|
||||
|
||||
#define MV_TWSI_NAME "twsi"
|
||||
#define IICBUS_DEVNAME "iicbus"
|
||||
|
||||
#define TWSI_ADDR 0x00
|
||||
#define TWSI_DATA 0x04
|
||||
#define TWSI_CNTR 0x08
|
||||
#define TWSI_XADDR 0x10
|
||||
#define TWSI_STAT 0x0c
|
||||
#define TWSI_BAUD_RATE 0x0c
|
||||
#define TWSI_SRST 0x1c
|
||||
|
||||
#define TWSI_BAUD_RATE_RAW(C,M,N) ((C)/((10*(M+1))<<(N+1)))
|
||||
#define TWSI_BAUD_RATE_SLOW 50000 /* 50kHz */
|
||||
#define TWSI_BAUD_RATE_FAST 100000 /* 100kHz */
|
||||
|
||||
#define TWSI_DEBUG
|
||||
#undef TWSI_DEBUG
|
||||
|
||||
#ifdef TWSI_DEBUG
|
||||
#define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt,##args); } while (0)
|
||||
#else
|
||||
#define debugf(fmt, args...)
|
||||
#endif
|
||||
|
||||
static int mv_twsi_probe(device_t);
|
||||
static int mv_twsi_attach(device_t);
|
||||
|
||||
static struct ofw_compat_data compat_data[] = {
|
||||
{ "mrvl,twsi", true },
|
||||
{ "marvell,mv64xxx-i2c", true },
|
||||
{ NULL, false }
|
||||
};
|
||||
|
||||
static device_method_t mv_twsi_methods[] = {
|
||||
/* device interface */
|
||||
DEVMETHOD(device_probe, mv_twsi_probe),
|
||||
DEVMETHOD(device_attach, mv_twsi_attach),
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
DEFINE_CLASS_1(twsi, mv_twsi_driver, mv_twsi_methods,
|
||||
sizeof(struct twsi_softc), twsi_driver);
|
||||
|
||||
static devclass_t mv_twsi_devclass;
|
||||
|
||||
DRIVER_MODULE(twsi, simplebus, mv_twsi_driver, mv_twsi_devclass, 0, 0);
|
||||
DRIVER_MODULE(iicbus, twsi, iicbus_driver, iicbus_devclass, 0, 0);
|
||||
MODULE_DEPEND(twsi, iicbus, 1, 1, 1);
|
||||
|
||||
static int
|
||||
mv_twsi_probe(device_t dev)
|
||||
{
|
||||
struct twsi_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
if (!ofw_bus_status_okay(dev))
|
||||
return (ENXIO);
|
||||
|
||||
if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
|
||||
return (ENXIO);
|
||||
|
||||
sc->reg_data = TWSI_DATA;
|
||||
sc->reg_slave_addr = TWSI_ADDR;
|
||||
sc->reg_slave_ext_addr = TWSI_XADDR;
|
||||
sc->reg_control = TWSI_CNTR;
|
||||
sc->reg_status = TWSI_STAT;
|
||||
sc->reg_baud_rate = TWSI_BAUD_RATE;
|
||||
sc->reg_soft_reset = TWSI_SRST;
|
||||
|
||||
device_set_desc(dev, "Marvell Integrated I2C Bus Controller");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
#define ABSSUB(a,b) (((a) > (b)) ? (a) - (b) : (b) - (a))
|
||||
static void
|
||||
mv_twsi_cal_baud_rate(const uint32_t target, struct twsi_baud_rate *rate)
|
||||
{
|
||||
uint32_t clk, cur, diff, diff0;
|
||||
int m, n, m0, n0;
|
||||
|
||||
/* Calculate baud rate. */
|
||||
m0 = n0 = 4; /* Default values on reset */
|
||||
diff0 = 0xffffffff;
|
||||
clk = get_tclk();
|
||||
|
||||
for (n = 0; n < 8; n++) {
|
||||
for (m = 0; m < 16; m++) {
|
||||
cur = TWSI_BAUD_RATE_RAW(clk,m,n);
|
||||
diff = ABSSUB(target, cur);
|
||||
if (diff < diff0) {
|
||||
m0 = m;
|
||||
n0 = n;
|
||||
diff0 = diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
rate->raw = TWSI_BAUD_RATE_RAW(clk, m0, n0);
|
||||
rate->param = TWSI_BAUD_RATE_PARAM(m0, n0);
|
||||
rate->m = m0;
|
||||
rate->n = n0;
|
||||
}
|
||||
|
||||
static int
|
||||
mv_twsi_attach(device_t dev)
|
||||
{
|
||||
struct twsi_softc *sc;
|
||||
phandle_t child, iicbusnode;
|
||||
device_t childdev;
|
||||
struct iicbus_ivar *devi;
|
||||
char dname[32]; /* 32 is taken from struct u_device */
|
||||
uint32_t paddr;
|
||||
int len, error, ret;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
mv_twsi_cal_baud_rate(TWSI_BAUD_RATE_SLOW, &sc->baud_rate[IIC_SLOW]);
|
||||
mv_twsi_cal_baud_rate(TWSI_BAUD_RATE_FAST, &sc->baud_rate[IIC_FAST]);
|
||||
if (bootverbose)
|
||||
device_printf(dev, "calculated baud rates are:\n"
|
||||
" %" PRIu32 " kHz (M=%d, N=%d) for slow,\n"
|
||||
" %" PRIu32 " kHz (M=%d, N=%d) for fast.\n",
|
||||
sc->baud_rate[IIC_SLOW].raw / 1000,
|
||||
sc->baud_rate[IIC_SLOW].m,
|
||||
sc->baud_rate[IIC_SLOW].n,
|
||||
sc->baud_rate[IIC_FAST].raw / 1000,
|
||||
sc->baud_rate[IIC_FAST].m,
|
||||
sc->baud_rate[IIC_FAST].n);
|
||||
|
||||
|
||||
ret = twsi_attach(dev);
|
||||
if (ret != 0)
|
||||
return (ret);
|
||||
|
||||
iicbusnode = 0;
|
||||
/* Find iicbus as the child devices in the device tree. */
|
||||
for (child = OF_child(ofw_bus_get_node(dev)); child != 0;
|
||||
child = OF_peer(child)) {
|
||||
len = OF_getproplen(child, "model");
|
||||
if (len <= 0 || len > sizeof(dname))
|
||||
continue;
|
||||
error = OF_getprop(child, "model", &dname, len);
|
||||
if (error == -1)
|
||||
continue;
|
||||
len = strlen(dname);
|
||||
if (len == strlen(IICBUS_DEVNAME) &&
|
||||
strncasecmp(dname, IICBUS_DEVNAME, len) == 0) {
|
||||
iicbusnode = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (iicbusnode == 0)
|
||||
goto attach_end;
|
||||
|
||||
/* Attach child devices onto iicbus. */
|
||||
for (child = OF_child(iicbusnode); child != 0; child = OF_peer(child)) {
|
||||
/* Get slave address. */
|
||||
error = OF_getprop(child, "i2c-address", &paddr, sizeof(paddr));
|
||||
if (error == -1)
|
||||
error = OF_getprop(child, "reg", &paddr, sizeof(paddr));
|
||||
if (error == -1)
|
||||
continue;
|
||||
|
||||
/* Get device driver name. */
|
||||
len = OF_getproplen(child, "model");
|
||||
if (len <= 0 || len > sizeof(dname))
|
||||
continue;
|
||||
OF_getprop(child, "model", &dname, len);
|
||||
|
||||
if (bootverbose)
|
||||
device_printf(dev, "adding a device %s at %d.\n",
|
||||
dname, fdt32_to_cpu(paddr));
|
||||
childdev = BUS_ADD_CHILD(sc->iicbus, 0, dname, -1);
|
||||
devi = IICBUS_IVAR(childdev);
|
||||
devi->addr = fdt32_to_cpu(paddr);
|
||||
}
|
||||
|
||||
attach_end:
|
||||
bus_generic_attach(sc->iicbus);
|
||||
|
||||
return (0);
|
||||
}
|
@ -31,7 +31,7 @@
|
||||
|
||||
/*
|
||||
* Driver for the TWSI (aka I2C, aka IIC) bus controller found on Marvell
|
||||
* SoCs. Supports master operation only, and works in polling mode.
|
||||
* and Allwinner SoCs. Supports master operation only, and works in polling mode.
|
||||
*
|
||||
* Calls to DELAY() are needed per Application Note AN-179 "TWSI Software
|
||||
* Guidelines for Discovery(TM), Horizon (TM) and Feroceon(TM) Devices".
|
||||
@ -62,156 +62,75 @@ __FBSDID("$FreeBSD$");
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include <arm/mv/mvvar.h>
|
||||
#include <dev/iicbus/twsi/twsi.h>
|
||||
|
||||
#include "iicbus_if.h"
|
||||
|
||||
#define MV_TWSI_NAME "twsi"
|
||||
#define IICBUS_DEVNAME "iicbus"
|
||||
#define TWSI_CONTROL_ACK (1 << 2)
|
||||
#define TWSI_CONTROL_IFLG (1 << 3)
|
||||
#define TWSI_CONTROL_STOP (1 << 4)
|
||||
#define TWSI_CONTROL_START (1 << 5)
|
||||
#define TWSI_CONTROL_TWSIEN (1 << 6)
|
||||
#define TWSI_CONTROL_INTEN (1 << 7)
|
||||
|
||||
#define TWSI_SLAVE_ADDR 0x00
|
||||
#define TWSI_EXT_SLAVE_ADDR 0x10
|
||||
#define TWSI_DATA 0x04
|
||||
#define TWSI_STATUS_START 0x08
|
||||
#define TWSI_STATUS_RPTD_START 0x10
|
||||
#define TWSI_STATUS_ADDR_W_ACK 0x18
|
||||
#define TWSI_STATUS_DATA_WR_ACK 0x28
|
||||
#define TWSI_STATUS_ADDR_R_ACK 0x40
|
||||
#define TWSI_STATUS_DATA_RD_ACK 0x50
|
||||
#define TWSI_STATUS_DATA_RD_NOACK 0x58
|
||||
|
||||
#define TWSI_CONTROL 0x08
|
||||
#define TWSI_CONTROL_ACK (1 << 2)
|
||||
#define TWSI_CONTROL_IFLG (1 << 3)
|
||||
#define TWSI_CONTROL_STOP (1 << 4)
|
||||
#define TWSI_CONTROL_START (1 << 5)
|
||||
#define TWSI_CONTROL_TWSIEN (1 << 6)
|
||||
#define TWSI_CONTROL_INTEN (1 << 7)
|
||||
|
||||
#define TWSI_STATUS 0x0c
|
||||
#define TWSI_STATUS_START 0x08
|
||||
#define TWSI_STATUS_RPTD_START 0x10
|
||||
#define TWSI_STATUS_ADDR_W_ACK 0x18
|
||||
#define TWSI_STATUS_DATA_WR_ACK 0x28
|
||||
#define TWSI_STATUS_ADDR_R_ACK 0x40
|
||||
#define TWSI_STATUS_DATA_RD_ACK 0x50
|
||||
#define TWSI_STATUS_DATA_RD_NOACK 0x58
|
||||
|
||||
#define TWSI_BAUD_RATE 0x0c
|
||||
#define TWSI_BAUD_RATE_PARAM(M,N) ((((M) << 3) | ((N) & 0x7)) & 0x7f)
|
||||
#define TWSI_BAUD_RATE_RAW(C,M,N) ((C)/((10*(M+1))<<(N+1)))
|
||||
#define TWSI_BAUD_RATE_SLOW 50000 /* 50kHz */
|
||||
#define TWSI_BAUD_RATE_FAST 100000 /* 100kHz */
|
||||
|
||||
#define TWSI_SOFT_RESET 0x1c
|
||||
|
||||
#define TWSI_DEBUG
|
||||
#define TWSI_DEBUG
|
||||
#undef TWSI_DEBUG
|
||||
|
||||
#ifdef TWSI_DEBUG
|
||||
#define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt,##args); } while (0)
|
||||
#ifdef TWSI_DEBUG
|
||||
#define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt,##args); } while (0)
|
||||
#else
|
||||
#define debugf(fmt, args...)
|
||||
#define debugf(fmt, args...)
|
||||
#endif
|
||||
|
||||
struct mv_twsi_softc {
|
||||
device_t dev;
|
||||
struct resource *res[1]; /* SYS_RES_MEMORY */
|
||||
struct mtx mutex;
|
||||
device_t iicbus;
|
||||
};
|
||||
|
||||
static struct mv_twsi_baud_rate {
|
||||
uint32_t raw;
|
||||
int param;
|
||||
int m;
|
||||
int n;
|
||||
} baud_rate[IIC_FASTEST + 1];
|
||||
|
||||
static int mv_twsi_probe(device_t);
|
||||
static int mv_twsi_attach(device_t);
|
||||
static int mv_twsi_detach(device_t);
|
||||
|
||||
static int mv_twsi_reset(device_t dev, u_char speed, u_char addr,
|
||||
u_char *oldaddr);
|
||||
static int mv_twsi_repeated_start(device_t dev, u_char slave, int timeout);
|
||||
static int mv_twsi_start(device_t dev, u_char slave, int timeout);
|
||||
static int mv_twsi_stop(device_t dev);
|
||||
static int mv_twsi_read(device_t dev, char *buf, int len, int *read, int last,
|
||||
int delay);
|
||||
static int mv_twsi_write(device_t dev, const char *buf, int len, int *sent,
|
||||
int timeout);
|
||||
|
||||
static struct resource_spec res_spec[] = {
|
||||
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
|
||||
{ -1, 0 }
|
||||
};
|
||||
|
||||
static struct ofw_compat_data compat_data[] = {
|
||||
{ "mrvl,twsi", true },
|
||||
{ "marvell,mv64xxx-i2c", true },
|
||||
{ NULL, false }
|
||||
};
|
||||
|
||||
static device_method_t mv_twsi_methods[] = {
|
||||
/* device interface */
|
||||
DEVMETHOD(device_probe, mv_twsi_probe),
|
||||
DEVMETHOD(device_attach, mv_twsi_attach),
|
||||
DEVMETHOD(device_detach, mv_twsi_detach),
|
||||
|
||||
/* iicbus interface */
|
||||
DEVMETHOD(iicbus_callback, iicbus_null_callback),
|
||||
DEVMETHOD(iicbus_repeated_start, mv_twsi_repeated_start),
|
||||
DEVMETHOD(iicbus_start, mv_twsi_start),
|
||||
DEVMETHOD(iicbus_stop, mv_twsi_stop),
|
||||
DEVMETHOD(iicbus_write, mv_twsi_write),
|
||||
DEVMETHOD(iicbus_read, mv_twsi_read),
|
||||
DEVMETHOD(iicbus_reset, mv_twsi_reset),
|
||||
DEVMETHOD(iicbus_transfer, iicbus_transfer_gen),
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static devclass_t mv_twsi_devclass;
|
||||
|
||||
static driver_t mv_twsi_driver = {
|
||||
MV_TWSI_NAME,
|
||||
mv_twsi_methods,
|
||||
sizeof(struct mv_twsi_softc),
|
||||
};
|
||||
|
||||
DRIVER_MODULE(twsi, simplebus, mv_twsi_driver, mv_twsi_devclass, 0, 0);
|
||||
DRIVER_MODULE(iicbus, twsi, iicbus_driver, iicbus_devclass, 0, 0);
|
||||
MODULE_DEPEND(twsi, iicbus, 1, 1, 1);
|
||||
|
||||
static __inline uint32_t
|
||||
TWSI_READ(struct mv_twsi_softc *sc, bus_size_t off)
|
||||
TWSI_READ(struct twsi_softc *sc, bus_size_t off)
|
||||
{
|
||||
|
||||
return (bus_read_4(sc->res[0], off));
|
||||
}
|
||||
|
||||
static __inline void
|
||||
TWSI_WRITE(struct mv_twsi_softc *sc, bus_size_t off, uint32_t val)
|
||||
TWSI_WRITE(struct twsi_softc *sc, bus_size_t off, uint32_t val)
|
||||
{
|
||||
|
||||
bus_write_4(sc->res[0], off, val);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
twsi_control_clear(struct mv_twsi_softc *sc, uint32_t mask)
|
||||
twsi_control_clear(struct twsi_softc *sc, uint32_t mask)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
val = TWSI_READ(sc, TWSI_CONTROL);
|
||||
val = TWSI_READ(sc, sc->reg_control);
|
||||
val &= ~mask;
|
||||
TWSI_WRITE(sc, TWSI_CONTROL, val);
|
||||
TWSI_WRITE(sc, sc->reg_control, val);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
twsi_control_set(struct mv_twsi_softc *sc, uint32_t mask)
|
||||
twsi_control_set(struct twsi_softc *sc, uint32_t mask)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
val = TWSI_READ(sc, TWSI_CONTROL);
|
||||
val = TWSI_READ(sc, sc->reg_control);
|
||||
val |= mask;
|
||||
TWSI_WRITE(sc, TWSI_CONTROL, val);
|
||||
TWSI_WRITE(sc, sc->reg_control, val);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
twsi_clear_iflg(struct mv_twsi_softc *sc)
|
||||
twsi_clear_iflg(struct twsi_softc *sc)
|
||||
{
|
||||
|
||||
DELAY(1000);
|
||||
@ -227,11 +146,11 @@ twsi_clear_iflg(struct mv_twsi_softc *sc)
|
||||
* non-zero on timeout
|
||||
*/
|
||||
static int
|
||||
twsi_poll_ctrl(struct mv_twsi_softc *sc, int timeout, uint32_t mask)
|
||||
twsi_poll_ctrl(struct twsi_softc *sc, int timeout, uint32_t mask)
|
||||
{
|
||||
|
||||
timeout /= 10;
|
||||
while (!(TWSI_READ(sc, TWSI_CONTROL) & mask)) {
|
||||
while (!(TWSI_READ(sc, sc->reg_control) & mask)) {
|
||||
DELAY(10);
|
||||
if (--timeout < 0)
|
||||
return (timeout);
|
||||
@ -247,7 +166,7 @@ twsi_poll_ctrl(struct mv_twsi_softc *sc, int timeout, uint32_t mask)
|
||||
* or TWSI_STATUS_RPTD_START
|
||||
*/
|
||||
static int
|
||||
twsi_locked_start(device_t dev, struct mv_twsi_softc *sc, int32_t mask,
|
||||
twsi_locked_start(device_t dev, struct twsi_softc *sc, int32_t mask,
|
||||
u_char slave, int timeout)
|
||||
{
|
||||
int read_access, iflg_set = 0;
|
||||
@ -257,7 +176,7 @@ twsi_locked_start(device_t dev, struct mv_twsi_softc *sc, int32_t mask,
|
||||
|
||||
if (mask == TWSI_STATUS_RPTD_START)
|
||||
/* read IFLG to know if it should be cleared later; from NBSD */
|
||||
iflg_set = TWSI_READ(sc, TWSI_CONTROL) & TWSI_CONTROL_IFLG;
|
||||
iflg_set = TWSI_READ(sc, sc->reg_control) & TWSI_CONTROL_IFLG;
|
||||
|
||||
twsi_control_set(sc, TWSI_CONTROL_START);
|
||||
|
||||
@ -278,14 +197,14 @@ twsi_locked_start(device_t dev, struct mv_twsi_softc *sc, int32_t mask,
|
||||
return (IIC_ETIMEOUT);
|
||||
}
|
||||
|
||||
status = TWSI_READ(sc, TWSI_STATUS);
|
||||
status = TWSI_READ(sc, sc->reg_status);
|
||||
if (status != mask) {
|
||||
debugf("wrong status (%02x) after sending %sSTART condition\n",
|
||||
status, mask == TWSI_STATUS_START ? "" : "repeated ");
|
||||
return (IIC_ESTATUS);
|
||||
}
|
||||
|
||||
TWSI_WRITE(sc, TWSI_DATA, slave);
|
||||
TWSI_WRITE(sc, sc->reg_data, slave);
|
||||
DELAY(1000);
|
||||
twsi_clear_iflg(sc);
|
||||
|
||||
@ -295,7 +214,7 @@ twsi_locked_start(device_t dev, struct mv_twsi_softc *sc, int32_t mask,
|
||||
}
|
||||
|
||||
read_access = (slave & 0x1) ? 1 : 0;
|
||||
status = TWSI_READ(sc, TWSI_STATUS);
|
||||
status = TWSI_READ(sc, sc->reg_status);
|
||||
if (status != (read_access ?
|
||||
TWSI_STATUS_ADDR_R_ACK : TWSI_STATUS_ADDR_W_ACK)) {
|
||||
debugf("no ACK (status: %02x) after sending slave address\n",
|
||||
@ -306,172 +225,13 @@ twsi_locked_start(device_t dev, struct mv_twsi_softc *sc, int32_t mask,
|
||||
return (IIC_NOERR);
|
||||
}
|
||||
|
||||
static int
|
||||
mv_twsi_probe(device_t dev)
|
||||
{
|
||||
|
||||
if (!ofw_bus_status_okay(dev))
|
||||
return (ENXIO);
|
||||
|
||||
if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, "Marvell Integrated I2C Bus Controller");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
#define ABSSUB(a,b) (((a) > (b)) ? (a) - (b) : (b) - (a))
|
||||
static void
|
||||
mv_twsi_cal_baud_rate(const uint32_t target, struct mv_twsi_baud_rate *rate)
|
||||
{
|
||||
uint32_t clk, cur, diff, diff0;
|
||||
int m, n, m0, n0;
|
||||
|
||||
/* Calculate baud rate. */
|
||||
m0 = n0 = 4; /* Default values on reset */
|
||||
diff0 = 0xffffffff;
|
||||
clk = get_tclk();
|
||||
|
||||
for (n = 0; n < 8; n++) {
|
||||
for (m = 0; m < 16; m++) {
|
||||
cur = TWSI_BAUD_RATE_RAW(clk,m,n);
|
||||
diff = ABSSUB(target, cur);
|
||||
if (diff < diff0) {
|
||||
m0 = m;
|
||||
n0 = n;
|
||||
diff0 = diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
rate->raw = TWSI_BAUD_RATE_RAW(clk, m0, n0);
|
||||
rate->param = TWSI_BAUD_RATE_PARAM(m0, n0);
|
||||
rate->m = m0;
|
||||
rate->n = n0;
|
||||
}
|
||||
|
||||
static int
|
||||
mv_twsi_attach(device_t dev)
|
||||
{
|
||||
struct mv_twsi_softc *sc;
|
||||
phandle_t child, iicbusnode;
|
||||
device_t childdev;
|
||||
struct iicbus_ivar *devi;
|
||||
char dname[32]; /* 32 is taken from struct u_device */
|
||||
uint32_t paddr;
|
||||
int len, error;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc->dev = dev;
|
||||
bzero(baud_rate, sizeof(baud_rate));
|
||||
|
||||
mtx_init(&sc->mutex, device_get_nameunit(dev), MV_TWSI_NAME, MTX_DEF);
|
||||
|
||||
/* Allocate IO resources */
|
||||
if (bus_alloc_resources(dev, res_spec, sc->res)) {
|
||||
device_printf(dev, "could not allocate resources\n");
|
||||
mv_twsi_detach(dev);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
mv_twsi_cal_baud_rate(TWSI_BAUD_RATE_SLOW, &baud_rate[IIC_SLOW]);
|
||||
mv_twsi_cal_baud_rate(TWSI_BAUD_RATE_FAST, &baud_rate[IIC_FAST]);
|
||||
if (bootverbose)
|
||||
device_printf(dev, "calculated baud rates are:\n"
|
||||
" %" PRIu32 " kHz (M=%d, N=%d) for slow,\n"
|
||||
" %" PRIu32 " kHz (M=%d, N=%d) for fast.\n",
|
||||
baud_rate[IIC_SLOW].raw / 1000,
|
||||
baud_rate[IIC_SLOW].m,
|
||||
baud_rate[IIC_SLOW].n,
|
||||
baud_rate[IIC_FAST].raw / 1000,
|
||||
baud_rate[IIC_FAST].m,
|
||||
baud_rate[IIC_FAST].n);
|
||||
|
||||
sc->iicbus = device_add_child(dev, IICBUS_DEVNAME, -1);
|
||||
if (sc->iicbus == NULL) {
|
||||
device_printf(dev, "could not add iicbus child\n");
|
||||
mv_twsi_detach(dev);
|
||||
return (ENXIO);
|
||||
}
|
||||
/* Attach iicbus. */
|
||||
bus_generic_attach(dev);
|
||||
|
||||
iicbusnode = 0;
|
||||
/* Find iicbus as the child devices in the device tree. */
|
||||
for (child = OF_child(ofw_bus_get_node(dev)); child != 0;
|
||||
child = OF_peer(child)) {
|
||||
len = OF_getproplen(child, "model");
|
||||
if (len <= 0 || len > sizeof(dname))
|
||||
continue;
|
||||
error = OF_getprop(child, "model", &dname, len);
|
||||
if (error == -1)
|
||||
continue;
|
||||
len = strlen(dname);
|
||||
if (len == strlen(IICBUS_DEVNAME) &&
|
||||
strncasecmp(dname, IICBUS_DEVNAME, len) == 0) {
|
||||
iicbusnode = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (iicbusnode == 0)
|
||||
goto attach_end;
|
||||
|
||||
/* Attach child devices onto iicbus. */
|
||||
for (child = OF_child(iicbusnode); child != 0; child = OF_peer(child)) {
|
||||
/* Get slave address. */
|
||||
error = OF_getprop(child, "i2c-address", &paddr, sizeof(paddr));
|
||||
if (error == -1)
|
||||
error = OF_getprop(child, "reg", &paddr, sizeof(paddr));
|
||||
if (error == -1)
|
||||
continue;
|
||||
|
||||
/* Get device driver name. */
|
||||
len = OF_getproplen(child, "model");
|
||||
if (len <= 0 || len > sizeof(dname))
|
||||
continue;
|
||||
OF_getprop(child, "model", &dname, len);
|
||||
|
||||
if (bootverbose)
|
||||
device_printf(dev, "adding a device %s at %d.\n",
|
||||
dname, fdt32_to_cpu(paddr));
|
||||
childdev = BUS_ADD_CHILD(sc->iicbus, 0, dname, -1);
|
||||
devi = IICBUS_IVAR(childdev);
|
||||
devi->addr = fdt32_to_cpu(paddr);
|
||||
}
|
||||
|
||||
attach_end:
|
||||
bus_generic_attach(sc->iicbus);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
mv_twsi_detach(device_t dev)
|
||||
{
|
||||
struct mv_twsi_softc *sc;
|
||||
int rv;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
if ((rv = bus_generic_detach(dev)) != 0)
|
||||
return (rv);
|
||||
|
||||
if (sc->iicbus != NULL)
|
||||
if ((rv = device_delete_child(dev, sc->iicbus)) != 0)
|
||||
return (rv);
|
||||
|
||||
bus_release_resources(dev, res_spec, sc->res);
|
||||
|
||||
mtx_destroy(&sc->mutex);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Only slave mode supported, disregard [old]addr
|
||||
*/
|
||||
static int
|
||||
mv_twsi_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
|
||||
twsi_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
|
||||
{
|
||||
struct mv_twsi_softc *sc;
|
||||
struct twsi_softc *sc;
|
||||
uint32_t param;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
@ -479,75 +239,30 @@ mv_twsi_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
|
||||
switch (speed) {
|
||||
case IIC_SLOW:
|
||||
case IIC_FAST:
|
||||
param = baud_rate[speed].param;
|
||||
param = sc->baud_rate[speed].param;
|
||||
break;
|
||||
case IIC_FASTEST:
|
||||
case IIC_UNKNOWN:
|
||||
default:
|
||||
param = baud_rate[IIC_FAST].param;
|
||||
param = sc->baud_rate[IIC_FAST].param;
|
||||
break;
|
||||
}
|
||||
|
||||
mtx_lock(&sc->mutex);
|
||||
TWSI_WRITE(sc, TWSI_SOFT_RESET, 0x0);
|
||||
TWSI_WRITE(sc, sc->reg_soft_reset, 0x0);
|
||||
DELAY(2000);
|
||||
TWSI_WRITE(sc, TWSI_BAUD_RATE, param);
|
||||
TWSI_WRITE(sc, TWSI_CONTROL, TWSI_CONTROL_TWSIEN | TWSI_CONTROL_ACK);
|
||||
TWSI_WRITE(sc, sc->reg_baud_rate, param);
|
||||
TWSI_WRITE(sc, sc->reg_control, TWSI_CONTROL_TWSIEN | TWSI_CONTROL_ACK);
|
||||
DELAY(1000);
|
||||
mtx_unlock(&sc->mutex);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* timeout is given in us
|
||||
*/
|
||||
static int
|
||||
mv_twsi_repeated_start(device_t dev, u_char slave, int timeout)
|
||||
twsi_stop(device_t dev)
|
||||
{
|
||||
struct mv_twsi_softc *sc;
|
||||
int rv;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
mtx_lock(&sc->mutex);
|
||||
rv = twsi_locked_start(dev, sc, TWSI_STATUS_RPTD_START, slave,
|
||||
timeout);
|
||||
mtx_unlock(&sc->mutex);
|
||||
|
||||
if (rv) {
|
||||
mv_twsi_stop(dev);
|
||||
return (rv);
|
||||
} else
|
||||
return (IIC_NOERR);
|
||||
}
|
||||
|
||||
/*
|
||||
* timeout is given in us
|
||||
*/
|
||||
static int
|
||||
mv_twsi_start(device_t dev, u_char slave, int timeout)
|
||||
{
|
||||
struct mv_twsi_softc *sc;
|
||||
int rv;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
mtx_lock(&sc->mutex);
|
||||
rv = twsi_locked_start(dev, sc, TWSI_STATUS_START, slave, timeout);
|
||||
mtx_unlock(&sc->mutex);
|
||||
|
||||
if (rv) {
|
||||
mv_twsi_stop(dev);
|
||||
return (rv);
|
||||
} else
|
||||
return (IIC_NOERR);
|
||||
}
|
||||
|
||||
static int
|
||||
mv_twsi_stop(device_t dev)
|
||||
{
|
||||
struct mv_twsi_softc *sc;
|
||||
struct twsi_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
@ -560,10 +275,55 @@ mv_twsi_stop(device_t dev)
|
||||
return (IIC_NOERR);
|
||||
}
|
||||
|
||||
/*
|
||||
* timeout is given in us
|
||||
*/
|
||||
static int
|
||||
mv_twsi_read(device_t dev, char *buf, int len, int *read, int last, int delay)
|
||||
twsi_repeated_start(device_t dev, u_char slave, int timeout)
|
||||
{
|
||||
struct mv_twsi_softc *sc;
|
||||
struct twsi_softc *sc;
|
||||
int rv;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
mtx_lock(&sc->mutex);
|
||||
rv = twsi_locked_start(dev, sc, TWSI_STATUS_RPTD_START, slave,
|
||||
timeout);
|
||||
mtx_unlock(&sc->mutex);
|
||||
|
||||
if (rv) {
|
||||
twsi_stop(dev);
|
||||
return (rv);
|
||||
} else
|
||||
return (IIC_NOERR);
|
||||
}
|
||||
|
||||
/*
|
||||
* timeout is given in us
|
||||
*/
|
||||
static int
|
||||
twsi_start(device_t dev, u_char slave, int timeout)
|
||||
{
|
||||
struct twsi_softc *sc;
|
||||
int rv;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
mtx_lock(&sc->mutex);
|
||||
rv = twsi_locked_start(dev, sc, TWSI_STATUS_START, slave, timeout);
|
||||
mtx_unlock(&sc->mutex);
|
||||
|
||||
if (rv) {
|
||||
twsi_stop(dev);
|
||||
return (rv);
|
||||
} else
|
||||
return (IIC_NOERR);
|
||||
}
|
||||
|
||||
static int
|
||||
twsi_read(device_t dev, char *buf, int len, int *read, int last, int delay)
|
||||
{
|
||||
struct twsi_softc *sc;
|
||||
uint32_t status;
|
||||
int last_byte, rv;
|
||||
|
||||
@ -591,7 +351,7 @@ mv_twsi_read(device_t dev, char *buf, int len, int *read, int last, int delay)
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = TWSI_READ(sc, TWSI_STATUS);
|
||||
status = TWSI_READ(sc, sc->reg_status);
|
||||
if (status != (last_byte ?
|
||||
TWSI_STATUS_DATA_RD_NOACK : TWSI_STATUS_DATA_RD_ACK)) {
|
||||
debugf("wrong status (%02x) while reading\n", status);
|
||||
@ -599,7 +359,7 @@ mv_twsi_read(device_t dev, char *buf, int len, int *read, int last, int delay)
|
||||
goto out;
|
||||
}
|
||||
|
||||
*buf++ = TWSI_READ(sc, TWSI_DATA);
|
||||
*buf++ = TWSI_READ(sc, sc->reg_data);
|
||||
(*read)++;
|
||||
}
|
||||
rv = IIC_NOERR;
|
||||
@ -609,9 +369,9 @@ mv_twsi_read(device_t dev, char *buf, int len, int *read, int last, int delay)
|
||||
}
|
||||
|
||||
static int
|
||||
mv_twsi_write(device_t dev, const char *buf, int len, int *sent, int timeout)
|
||||
twsi_write(device_t dev, const char *buf, int len, int *sent, int timeout)
|
||||
{
|
||||
struct mv_twsi_softc *sc;
|
||||
struct twsi_softc *sc;
|
||||
uint32_t status;
|
||||
int rv;
|
||||
|
||||
@ -620,7 +380,7 @@ mv_twsi_write(device_t dev, const char *buf, int len, int *sent, int timeout)
|
||||
mtx_lock(&sc->mutex);
|
||||
*sent = 0;
|
||||
while (*sent < len) {
|
||||
TWSI_WRITE(sc, TWSI_DATA, *buf++);
|
||||
TWSI_WRITE(sc, sc->reg_data, *buf++);
|
||||
|
||||
twsi_clear_iflg(sc);
|
||||
if (twsi_poll_ctrl(sc, timeout, TWSI_CONTROL_IFLG)) {
|
||||
@ -629,7 +389,7 @@ mv_twsi_write(device_t dev, const char *buf, int len, int *sent, int timeout)
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = TWSI_READ(sc, TWSI_STATUS);
|
||||
status = TWSI_READ(sc, sc->reg_status);
|
||||
if (status != TWSI_STATUS_DATA_WR_ACK) {
|
||||
debugf("wrong status (%02x) while writing\n", status);
|
||||
rv = IIC_ESTATUS;
|
||||
@ -642,3 +402,81 @@ mv_twsi_write(device_t dev, const char *buf, int len, int *sent, int timeout)
|
||||
mtx_unlock(&sc->mutex);
|
||||
return (rv);
|
||||
}
|
||||
|
||||
int
|
||||
twsi_attach(device_t dev)
|
||||
{
|
||||
struct twsi_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc->dev = dev;
|
||||
|
||||
mtx_init(&sc->mutex, device_get_nameunit(dev), "twsi", MTX_DEF);
|
||||
|
||||
if (bus_alloc_resources(dev, res_spec, sc->res)) {
|
||||
device_printf(dev, "could not allocate resources\n");
|
||||
twsi_detach(dev);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/* Attach the iicbus. */
|
||||
if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL) {
|
||||
device_printf(dev, "could not allocate iicbus instance\n");
|
||||
twsi_detach(dev);
|
||||
return (ENXIO);
|
||||
}
|
||||
bus_generic_attach(dev);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
twsi_detach(device_t dev)
|
||||
{
|
||||
struct twsi_softc *sc;
|
||||
int rv;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
if ((rv = bus_generic_detach(dev)) != 0)
|
||||
return (rv);
|
||||
|
||||
if (sc->iicbus != NULL)
|
||||
if ((rv = device_delete_child(dev, sc->iicbus)) != 0)
|
||||
return (rv);
|
||||
|
||||
bus_release_resources(dev, res_spec, sc->res);
|
||||
|
||||
mtx_destroy(&sc->mutex);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static device_method_t twsi_methods[] = {
|
||||
/* device interface */
|
||||
DEVMETHOD(device_detach, twsi_detach),
|
||||
|
||||
/* Bus interface */
|
||||
DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
|
||||
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
|
||||
DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
|
||||
DEVMETHOD(bus_release_resource, bus_generic_release_resource),
|
||||
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
|
||||
DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
|
||||
DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource),
|
||||
DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource),
|
||||
DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource),
|
||||
|
||||
/* iicbus interface */
|
||||
DEVMETHOD(iicbus_callback, iicbus_null_callback),
|
||||
DEVMETHOD(iicbus_repeated_start, twsi_repeated_start),
|
||||
DEVMETHOD(iicbus_start, twsi_start),
|
||||
DEVMETHOD(iicbus_stop, twsi_stop),
|
||||
DEVMETHOD(iicbus_write, twsi_write),
|
||||
DEVMETHOD(iicbus_read, twsi_read),
|
||||
DEVMETHOD(iicbus_reset, twsi_reset),
|
||||
DEVMETHOD(iicbus_transfer, iicbus_transfer_gen),
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
DEFINE_CLASS_0(twsi, twsi_driver, twsi_methods,
|
||||
sizeof(struct twsi_softc));
|
||||
|
67
sys/dev/iicbus/twsi/twsi.h
Normal file
67
sys/dev/iicbus/twsi/twsi.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*-
|
||||
* Copyright (C) 2008 MARVELL INTERNATIONAL LTD.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by Semihalf.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of MARVELL nor the names of contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _TWSI_H_
|
||||
#define _TWSI_H_
|
||||
|
||||
struct twsi_baud_rate {
|
||||
uint32_t raw;
|
||||
int param;
|
||||
int m;
|
||||
int n;
|
||||
};
|
||||
|
||||
struct twsi_softc {
|
||||
device_t dev;
|
||||
struct resource *res[1]; /* SYS_RES_MEMORY */
|
||||
struct mtx mutex;
|
||||
device_t iicbus;
|
||||
|
||||
bus_size_t reg_data;
|
||||
bus_size_t reg_slave_addr;
|
||||
bus_size_t reg_slave_ext_addr;
|
||||
bus_size_t reg_control;
|
||||
bus_size_t reg_status;
|
||||
bus_size_t reg_baud_rate;
|
||||
bus_size_t reg_soft_reset;
|
||||
struct twsi_baud_rate baud_rate[IIC_FASTEST + 1];
|
||||
};
|
||||
|
||||
DECLARE_CLASS(twsi_driver);
|
||||
|
||||
#define TWSI_BAUD_RATE_PARAM(M,N) ((((M) << 3) | ((N) & 0x7)) & 0x7f)
|
||||
|
||||
int twsi_attach(device_t);
|
||||
int twsi_detach(device_t);
|
||||
|
||||
#endif /* _TWSI_H_ */
|
Loading…
Reference in New Issue
Block a user