/*-
* Copyright (c) 2006 Urbana-Champaign Independent Media Center.
* Copyright (c) 2006 Garrett D'Amore.
* All rights reserved.
*
* Portions of this code were written by Garrett D'Amore for the
* Champaign-Urbana Community Wireless Network Project.
*
* 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. All advertising materials mentioning features or use of this
* software must display the following acknowledgements:
* This product includes software developed by the Urbana-Champaign
* Independent Media Center.
* This product includes software developed by Garrett D'Amore.
* 4. Urbana-Champaign Independent Media Center's name and Garrett
* D'Amore's name may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE URBANA-CHAMPAIGN INDEPENDENT
* MEDIA CENTER AND GARRETT D'AMORE ``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 URBANA-CHAMPAIGN INDEPENDENT
* MEDIA CENTER OR GARRETT D'AMORE 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.
*/
/*
* SPI slave device. We have one of these per slave.
*/
struct spi_handle {
struct spi_softc *sh_sc;
struct spi_controller *sh_controller;
int sh_slave;
int sh_mode;
int sh_speed;
int sh_flags;
#define SPIH_ATTACHED 1
};
#define SPI_MAXDATA 4096
/*
* API for bus drivers.
*/
int
spibus_print(void *aux, const char *pnp)
{
if (pnp != NULL)
aprint_normal("spi at %s", pnp);
return (UNCONF);
}
static int
spi_match(device_t parent, cfdata_t cf, void *aux)
{
/*
* XXX this is the same as i2c_fill_compat. It could be refactored into a
* common fill_compat function with pointers to compat & ncompat instead
* of attach_args as the first parameter.
*/
static void
spi_fill_compat(struct spi_attach_args *sa, const char *compat, size_t len,
char **buffer)
{
int count, i;
const char *c, *start, **ptr;
*buffer = NULL;
for (i = count = 0, c = compat; i < len; i++, c++)
if (*c == 0)
count++;
count += 2;
ptr = malloc(sizeof(char*)*count, M_TEMP, M_WAITOK);
if (!ptr)
return;
for (i = count = 0, start = c = compat; i < len; i++, c++) {
if (*c == 0) {
ptr[count++] = start;
start = c + 1;
}
}
if (start < compat + len) {
/* last string not 0 terminated */
size_t l = c - start;
*buffer = malloc(l + 1, M_TEMP, M_WAITOK);
memcpy(*buffer, start, l);
(*buffer)[l] = 0;
ptr[count++] = *buffer;
}
ptr[count] = NULL;
sa->sa_compat = ptr;
sa->sa_ncompat = count;
}
static void
spi_direct_attach_child_devices(device_t parent, struct spi_softc *sc,
prop_array_t child_devices)
{
unsigned int count;
prop_dictionary_t child;
prop_data_t cdata;
uint32_t slave;
uint64_t cookie;
struct spi_attach_args sa;
int loc[SPICF_NLOCS];
char *buf;
int i;
memset(loc, 0, sizeof loc);
count = prop_array_count(child_devices);
for (i = 0; i < count; i++) {
child = prop_array_get(child_devices, i);
if (!child)
continue;
if (!prop_dictionary_get_uint32(child, "slave", &slave))
continue;
if(slave >= sc->sc_controller.sct_nslaves)
continue;
if (!prop_dictionary_get_uint64(child, "cookie", &cookie))
continue;
if (!(cdata = prop_dictionary_get(child, "compatible")))
continue;
loc[SPICF_SLAVE] = slave;
/*
* API for device drivers.
*
* We provide wrapper routines to decouple the ABI for the SPI
* device drivers from the ABI for the SPI bus drivers.
*/
static void
spi_attach(device_t parent, device_t self, void *aux)
{
struct spi_softc *sc = device_private(self);
struct spibus_attach_args *sba = aux;
int i;
/*
* Initialize slave handles
*/
for (i = 0; i < sc->sc_nslaves; i++) {
sc->sc_slaves[i].sh_slave = i;
sc->sc_slaves[i].sh_sc = sc;
sc->sc_slaves[i].sh_controller = &sc->sc_controller;
}
/* First attach devices known to be present via fdt */
if (sba->sba_child_devices) {
spi_direct_attach_child_devices(self, sc, sba->sba_child_devices);
}
/* Then do any other devices the user may have manually wired */
config_search(self, NULL,
CFARGS(.search = spi_search));
}
static int
spi_open(dev_t dev, int flag, int fmt, lwp_t *l)
{
struct spi_softc *sc = device_lookup_private(&spi_cd, minor(dev));
if (sc == NULL)
return ENXIO;
return 0;
}
static int
spi_close(dev_t dev, int flag, int fmt, lwp_t *l)
{
/*
* Configure. This should be the first thing that the SPI driver
* should do, to configure which mode (e.g. SPI_MODE_0, which is the
* same as Philips Microwire mode), and speed. If the bus driver
* cannot run fast enough, then it should just configure the fastest
* mode that it can support. If the bus driver cannot run slow
* enough, then the device is incompatible and an error should be
* returned.
*/
int
spi_configure(device_t dev __unused, struct spi_handle *sh, int mode, int speed)
{
/*
* Initialize "resid" counters and pointers, so that callers
* and bus drivers don't have to.
*/
for (chunk = st->st_chunks; chunk; chunk = chunk->chunk_next) {
chunk->chunk_wresid = chunk->chunk_rresid = chunk->chunk_count;
chunk->chunk_wptr = chunk->chunk_write;
chunk->chunk_rptr = chunk->chunk_read;
}
/*
* Match slave and parameters to handle
*/
st->st_slave = sh->sh_slave;
/*
* Reserve controller during transaction
*/
spi_acquire(sh);
st->st_spiprivate = (void *)sh;
/*
* Reconfigure controller
*
* XXX backends don't configure per-slave parameters
* Whenever we switch slaves or change mode or speed, we
* need to tell the backend.
*/
if (sc->sc_slave != sh->sh_slave
|| sc->sc_mode != sh->sh_mode
|| sc->sc_speed != sh->sh_speed) {
error = (*tag->sct_configure)(tag->sct_cookie,
sh->sh_slave, sh->sh_mode, sh->sh_speed);
if (error)
return error;
}
sc->sc_mode = sh->sh_mode;
sc->sc_speed = sh->sh_speed;
sc->sc_slave = sh->sh_slave;
/*
* Some convenience routines. These routines block until the work
* is done.
*
* spi_recv - receives data from the bus
*
* spi_send - sends data to the bus
*
* spi_send_recv - sends data to the bus, and then receives. Note that this is
* done synchronously, i.e. send a command and get the response. This is
* not full duplex. If you want full duplex, you can't use these convenience
* wrappers.
*/
int
spi_recv(struct spi_handle *sh, int cnt, uint8_t *data)
{
struct spi_transfer trans;
struct spi_chunk chunk;