NetBSD/sys/arch/arm/samsung/exynos_i2c.c
marty 7f537c15f8 EXYNOS Rewrite step 2 of N: New exynos_gpio.c
I can't bring  myself to fully nuke from orbit, so there are really two
things in this checkin:

1) A major rewrite of exynos_gpio.c, based mostly on the Nvidia
   tegra_gpio.c file.  This is missing a major function that will be
   added the first time a customer for it is integrated, which is meant to
   select pins based on aliases, rather than pin bank names.

2) A small number of changes to other files that keep the tree compiling
   and progressing as far as ever; except it is now 5422 specific and
   will not boot on the other exynos socs, which I don't have hardware to
   test.

The choice to remove functionality is always controversial, but since
we are doing a significant rewrite and I don't have either
documentation or hardware *and* none of the code really works now
anyway, I'm taking the stance that only tested functionality should be
added, and that we'll layer the other exynos socs on this once it
fully boots.
2015-12-11 04:03:44 +00:00

398 lines
10 KiB
C

/* $NetBSD: exynos_i2c.c,v 1.4 2015/12/11 04:03:44 marty Exp $ */
/*
* Copyright (c) 2014 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Reinoud Zandijk.
*
* 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 THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION 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 "opt_exynos.h"
#include "opt_arm_debug.h"
#include "exynos_iic.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: exynos_i2c.c,v 1.4 2015/12/11 04:03:44 marty Exp $");
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/device.h>
#include <sys/intr.h>
#include <sys/systm.h>
#include <sys/kmem.h>
#include <arm/samsung/exynos_reg.h>
#include <arm/samsung/exynos_io.h>
#include <arm/samsung/exynos_intr.h>
#include <sys/gpio.h>
#include <dev/gpio/gpiovar.h>
#include <dev/i2c/i2cvar.h>
#include <dev/i2c/i2c_bitbang.h>
struct exynos_iic_dev_softc {
int isc_bus;
bool isc_isgpio;
bus_space_tag_t isc_bst;
bus_space_handle_t isc_bsh;
struct exynos_gpio_pinset isc_pinset;
struct exynos_gpio_pindata isc_sda;
struct exynos_gpio_pindata isc_slc;
bool isc_sda_is_output;
kmutex_t isc_buslock;
struct i2c_controller isc_i2cbus;
};
#if NEXYNOS_IIC > 0
static int exynos_iic_acquire_bus(void *, int);
static void exynos_iic_release_bus(void *, int);
static int exynos_iic_send_start(void *, int);
static int exynos_iic_send_stop(void *, int);
static int exynos_iic_initiate_xfer(void *, i2c_addr_t, int);
static int exynos_iic_read_byte(void *, uint8_t *, int);
static int exynos_iic_write_byte(void *, uint8_t , int);
static bool exynos_iic_attach_i2cbus(struct exynos_iic_dev_softc *,
struct i2c_controller *, struct exyo_locators const *);
#endif
struct i2c_controller *exynos_i2cbus[EXYNOS_MAX_IIC_BUSSES];
static int exynos_iic_match(device_t, cfdata_t, void *);
static void exynos_iic_attach(device_t, device_t, void *);
struct exynos_iic_softc {
device_t sc_dev;
struct exynos_iic_dev_softc sc_idevs[EXYNOS_MAX_IIC_BUSSES];
struct i2c_controller sc_i2cbus[EXYNOS_MAX_IIC_BUSSES];
} exynos_iic_sc;
CFATTACH_DECL_NEW(exynos_iic, sizeof(struct exynos_iic_softc),
exynos_iic_match, exynos_iic_attach, NULL, NULL);
static int
exynos_iic_match(device_t self, cfdata_t cf, void *aux)
{
#ifdef DIAGNOSTIC
struct exyo_attach_args *exyoaa = aux;
struct exyo_locators *loc = &exyoaa->exyo_loc;
#endif
int i;
/* no locators expected */
KASSERT(loc->loc_offset == 0);
KASSERT(loc->loc_size == 0);
KASSERT(loc->loc_port == EXYOCF_PORT_DEFAULT);
if (exynos_iic_sc.sc_dev != NULL)
return 0;
for (i = 0; i < EXYNOS_MAX_IIC_BUSSES; i++)
exynos_i2cbus[i] = NULL;
#if NEXYNOS_IIC > 0
return 1;
#else
return 0;
#endif
}
static void
exynos_iic_attach(device_t parent, device_t self, void *aux)
{
#if NEXYNOS_IIC > 0
prop_dictionary_t dict = device_properties(self);
struct exynos_iic_softc * const sc = device_private(self);
struct exyo_attach_args * const exyoaa = aux;
struct exynos_iic_dev_softc *ei2c_sc;
struct i2c_controller *i2c_cntr;
struct i2cbus_attach_args iba;
struct exyo_locinfo const *locs;
struct exyo_locators const *loc;
bool enable;
char scrap[strlen("iic??_enable")];
int i;
locs = NULL;
#ifdef EXYNOS4
if (IS_EXYNOS4_P())
locs = &exynos4_i2c_locinfo;
#endif
#ifdef EXYNOS5
if (IS_EXYNOS5_P())
locs = &exynos5_i2c_locinfo;
#endif
KASSERT(locs);
sc->sc_dev = self;
if (locs->nlocators == 0) {
aprint_error(": no i2c busses defined\n");
return;
}
aprint_normal("\n");
for (i = 0; i < locs->nlocators; i++) {
/* get subdriver type : either gpio or hw */
snprintf(scrap, sizeof(scrap), "iic%d_enable", i);
if (prop_dictionary_get_bool(dict, scrap, &enable)) {
if (!enable)
continue;
/* found an iic device */
ei2c_sc = &sc->sc_idevs[i];
i2c_cntr = &sc->sc_i2cbus[i];
loc = &locs->locators[i];
ei2c_sc->isc_bus = i;
ei2c_sc->isc_isgpio = (loc->loc_flags > 0);
mutex_init(&ei2c_sc->isc_buslock, MUTEX_DEFAULT, IPL_NONE);
ei2c_sc->isc_bst = exyoaa->exyo_core_bst;
if (bus_space_subregion(ei2c_sc->isc_bst,
exyoaa->exyo_core_bsh,
loc->loc_offset, loc->loc_size,
&ei2c_sc->isc_bsh)) {
aprint_error_dev(self,
": failed to map registers for i2cbus%d\n",
i);
continue;
}
if (!exynos_iic_attach_i2cbus(ei2c_sc, i2c_cntr, loc))
continue;
exynos_i2cbus[i] = i2c_cntr;
iba.iba_tag = i2c_cntr;
(void) config_found_ia(sc->sc_dev, "i2cbus", &iba,
iicbus_print);
}
}
#endif
}
#if NEXYNOS_IIC > 0
static bool
exynos_iic_attach_i2cbus(struct exynos_iic_dev_softc *ei2c_sc,
struct i2c_controller *i2c_cntr, struct exyo_locators const *loc)
{
struct exynos_gpio_pinset *pinset;
/* reserve our pins */
pinset = &ei2c_sc->isc_pinset;
strcpy(pinset->pinset_bank, loc->loc_gpio_bus);
pinset->pinset_mask = __BIT(loc->loc_sda) | __BIT(loc->loc_slc);
pinset->pinset_func = loc->loc_func;
i2c_cntr->ic_cookie = ei2c_sc;
i2c_cntr->ic_acquire_bus = exynos_iic_acquire_bus;
i2c_cntr->ic_release_bus = exynos_iic_release_bus;
i2c_cntr->ic_send_start = exynos_iic_send_start;
i2c_cntr->ic_send_stop = exynos_iic_send_stop;
i2c_cntr->ic_initiate_xfer = exynos_iic_initiate_xfer;
i2c_cntr->ic_read_byte = exynos_iic_read_byte;
i2c_cntr->ic_write_byte = exynos_iic_write_byte;
/*MJF: FIX ME IN REWRITE */
// exynos_gpio_pinset_acquire(pinset);
if (ei2c_sc->isc_isgpio) {
/* get sda and slc pins */
#if 0
exynos_gpio_pinset_to_pindata(pinset,
loc->loc_sda, &ei2c_sc->isc_sda);
exynos_gpio_pinset_to_pindata(pinset,
loc->loc_slc, &ei2c_sc->isc_slc);
ei2c_sc->isc_sda_is_output = false;
exynos_gpio_pindata_ctl(&ei2c_sc->isc_sda, GPIO_PIN_INPUT);
exynos_gpio_pindata_ctl(&ei2c_sc->isc_slc, GPIO_PIN_OUTPUT);
#endif
return 1;
} else {
/* TBD: attach hardware driver */
aprint_normal("i2cbus%d: would attach native i2c driver\n",
ei2c_sc->isc_bus);
return 0;
}
}
#define EXYNOS_IIC_BB_SDA __BIT(1)
#define EXYNOS_IIC_BB_SCL __BIT(2)
#define EXYNOS_IIC_BB_SDA_OUT __BIT(3)
#define EXYNOS_IIC_BB_SDA_IN 0
static void
exynos_iic_bb_set_bits(void *cookie, uint32_t bits)
{
struct exynos_iic_dev_softc *i2c_sc = cookie;
int sda, slc;
sda = (bits & EXYNOS_IIC_BB_SDA) ? true : false;
slc = (bits & EXYNOS_IIC_BB_SCL) ? true : false;
if (i2c_sc->isc_sda_is_output)
exynos_gpio_pindata_write(&i2c_sc->isc_sda, sda);
exynos_gpio_pindata_write(&i2c_sc->isc_slc, slc);
}
static uint32_t
exynos_iic_bb_read_bits(void *cookie)
{
struct exynos_iic_dev_softc *i2c_sc = cookie;
int sda, slc;
sda = 0;
if (!i2c_sc->isc_sda_is_output)
sda = exynos_gpio_pindata_read(&i2c_sc->isc_sda);
slc = exynos_gpio_pindata_read(&i2c_sc->isc_slc);
return (sda ? EXYNOS_IIC_BB_SDA : 0) | (slc ? EXYNOS_IIC_BB_SCL : 0);
}
static void
exynos_iic_bb_set_dir(void *cookie, uint32_t bits)
{
struct exynos_iic_dev_softc *i2c_sc = cookie;
int flags;
flags = GPIO_PIN_INPUT | GPIO_PIN_TRISTATE;
i2c_sc->isc_sda_is_output = ((bits & EXYNOS_IIC_BB_SDA_OUT) != 0);
if (i2c_sc->isc_sda_is_output)
flags = GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE;
exynos_gpio_pindata_ctl(&i2c_sc->isc_sda, flags);
}
static const struct i2c_bitbang_ops exynos_iic_bbops = {
exynos_iic_bb_set_bits,
exynos_iic_bb_set_dir,
exynos_iic_bb_read_bits,
{
EXYNOS_IIC_BB_SDA,
EXYNOS_IIC_BB_SCL,
EXYNOS_IIC_BB_SDA_OUT,
EXYNOS_IIC_BB_SDA_IN,
}
};
static int
exynos_iic_acquire_bus(void *cookie, int flags)
{
struct exynos_iic_dev_softc *i2c_sc = cookie;
/* XXX what to do in polling case? could another cpu help */
if (flags & I2C_F_POLL)
return 0;
mutex_enter(&i2c_sc->isc_buslock);
return 0;
}
static void
exynos_iic_release_bus(void *cookie, int flags)
{
struct exynos_iic_dev_softc *i2c_sc = cookie;
/* XXX what to do in polling case? could another cpu help */
if (flags & I2C_F_POLL)
return;
mutex_exit(&i2c_sc->isc_buslock);
}
static int
exynos_iic_send_start(void *cookie, int flags)
{
struct exynos_iic_dev_softc *i2c_sc = cookie;
if (i2c_sc->isc_isgpio)
return i2c_bitbang_send_start(cookie, flags, &exynos_iic_bbops);
panic("%s: not implemented for non gpio case\n", __func__);
return EINVAL;
}
static int
exynos_iic_send_stop(void *cookie, int flags)
{
struct exynos_iic_dev_softc *i2c_sc = cookie;
if (i2c_sc->isc_isgpio)
return i2c_bitbang_send_stop(cookie, flags, &exynos_iic_bbops);
panic("%s: not implemented for non gpio case\n", __func__);
return EINVAL;
}
static int
exynos_iic_initiate_xfer(void *cookie, i2c_addr_t addr, int flags)
{
struct exynos_iic_dev_softc *i2c_sc = cookie;
if (i2c_sc->isc_isgpio)
return i2c_bitbang_initiate_xfer(cookie, addr, flags,
&exynos_iic_bbops);
panic("%s: not implemented for non gpio case\n", __func__);
return EINVAL;
}
static int
exynos_iic_read_byte(void *cookie, uint8_t *bytep, int flags)
{
struct exynos_iic_dev_softc *i2c_sc = cookie;
if (i2c_sc->isc_isgpio)
return i2c_bitbang_read_byte(cookie, bytep, flags,
&exynos_iic_bbops);
panic("%s: not implemented for non gpio case\n", __func__);
return EINVAL;
}
static int
exynos_iic_write_byte(void *cookie, uint8_t byte, int flags)
{
struct exynos_iic_dev_softc *i2c_sc = cookie;
if (i2c_sc->isc_isgpio)
return i2c_bitbang_write_byte(cookie, byte, flags,
&exynos_iic_bbops);
panic("%s: not implemented for non gpio case\n", __func__);
return EINVAL;
}
#endif /* NEXYNOS_IIC > 0 */