spi: prepare for fdt direct attachment of spi slaves

Introduce sba_child_devices array in spibus_attach_args. If the parent has
populated sba_child_devices then attach them first. Then do any devices
devices the user has wired in the kernel config, if any.
This commit is contained in:
tnn 2019-08-13 16:37:15 +00:00
parent c3a403baa3
commit f5ccd4002e
2 changed files with 128 additions and 8 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: spi.c,v 1.11 2019/03/09 07:53:12 mlelstv Exp $ */
/* $NetBSD: spi.c,v 1.12 2019/08/13 16:37:15 tnn Exp $ */
/*-
* Copyright (c) 2006 Urbana-Champaign Independent Media Center.
@ -42,7 +42,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: spi.c,v 1.11 2019/03/09 07:53:12 mlelstv Exp $");
__KERNEL_RCSID(0, "$NetBSD: spi.c,v 1.12 2019/08/13 16:37:15 tnn Exp $");
#include "locators.h"
@ -102,6 +102,8 @@ struct spi_handle {
int sh_slave;
int sh_mode;
int sh_speed;
int sh_flags;
#define SPIH_ATTACHED 1
};
#define SPI_MAXDATA 4096
@ -151,14 +153,122 @@ spi_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
return -1;
}
memset(&sa, 0, sizeof sa);
sa.sa_handle = &sc->sc_slaves[addr];
if (ISSET(sa.sa_handle->sh_flags, SPIH_ATTACHED))
return -1;
if (config_match(parent, cf, &sa) > 0)
if (config_match(parent, cf, &sa) > 0) {
SET(sa.sa_handle->sh_flags, SPIH_ATTACHED);
config_attach(parent, cf, &sa, spi_print);
}
return 0;
}
/*
* 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;
memset(&sa, 0, sizeof sa);
sa.sa_handle = &sc->sc_slaves[i];
if (ISSET(sa.sa_handle->sh_flags, SPIH_ATTACHED))
continue;
SET(sa.sa_handle->sh_flags, SPIH_ATTACHED);
buf = NULL;
spi_fill_compat(&sa,
prop_data_data_nocopy(cdata),
prop_data_size(cdata), &buf);
(void) config_found_sm_loc(parent, "spi",
loc, &sa, spi_print,
NULL);
if (sa.sa_compat)
free(sa.sa_compat, M_TEMP);
if (buf)
free(buf, M_TEMP);
}
}
int
spi_compatible_match(const struct spi_attach_args *sa, const cfdata_t cf,
const struct device_compatible_entry *compats)
{
if (sa->sa_ncompat > 0)
return device_compatible_match(sa->sa_compat, sa->sa_ncompat,
compats, NULL);
return 1;
}
/*
* API for device drivers.
*
@ -197,9 +307,11 @@ spi_attach(device_t parent, device_t self, void *aux)
sc->sc_slaves[i].sh_controller = &sc->sc_controller;
}
/*
* Locate and attach child devices
*/
/* 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_ia(spi_search, self, "spi", NULL);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: spivar.h,v 1.7 2019/02/23 10:43:25 mlelstv Exp $ */
/* $NetBSD: spivar.h,v 1.8 2019/08/13 16:37:15 tnn Exp $ */
/*-
* Copyright (c) 2006 Urbana-Champaign Independent Media Center.
@ -77,10 +77,16 @@ int spibus_print(void *, const char *);
/* one per chip select */
struct spibus_attach_args {
struct spi_controller *sba_controller;
prop_array_t sba_child_devices;
};
struct spi_attach_args {
struct spi_handle *sa_handle;
/* only set if using direct config */
int sa_ncompat; /* number of pointers in the
ia_compat array */
const char ** sa_compat; /* chip names */
prop_dictionary_t sa_prop; /* dictionary for this device */
};
/*
@ -132,7 +138,9 @@ SIMPLEQ_HEAD(spi_transq, spi_transfer);
#define SPI_F_DONE 0x0001
#define SPI_F_ERROR 0x0002
int spi_configure(struct spi_handle *, int mode, int speed);
int spi_compatible_match(const struct spi_attach_args *, const cfdata_t,
const struct device_compatible_entry *);
int spi_configure(struct spi_handle *, int, int);
int spi_transfer(struct spi_handle *, struct spi_transfer *);
void spi_transfer_init(struct spi_transfer *);
void spi_chunk_init(struct spi_chunk *, int, const uint8_t *, uint8_t *);