bc153c692f
bus_get_cpus() returns a specified set of CPUs for a device. It accepts an enum for the second parameter that indicates the type of cpuset to request. Currently two valus are supported: - LOCAL_CPUS (on x86 this returns all the CPUs in the package closest to the device when DEVICE_NUMA is enabled) - INTR_CPUS (like LOCAL_CPUS but only returns 1 SMT thread for each core) For systems that do not support NUMA (or if it is not enabled in the kernel config), LOCAL_CPUS fails with EINVAL. INTR_CPUS is mapped to 'all_cpus' by default. The idea is that INTR_CPUS should always return a valid set. Device drivers which want to use per-CPU interrupts should start using INTR_CPUS instead of simply assigning interrupts to all available CPUs. In the future we may wish to add tunables to control the policy of INTR_CPUS (e.g. should it be local-only or global, should it ignore SMT threads or not). The x86 nexus driver exposes the internal set of interrupt CPUs from the the x86 interrupt code via INTR_CPUS. The ACPI bus driver and PCI bridge drivers use _PXM to return a suitable LOCAL_CPUS set when _PXM exists and DEVICE_NUMA is enabled. They also and the global INTR_CPUS set from the nexus driver with the per-domain set from _PXM to generate a local INTR_CPUS set for child devices. Reviewed by: wblock (manpage) Differential Revision: https://reviews.freebsd.org/D5519
279 lines
6.5 KiB
C
279 lines
6.5 KiB
C
/*
|
|
* Copyright © 2009 Keith Packard
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this software and its
|
|
* documentation for any purpose is hereby granted without fee, provided that
|
|
* the above copyright notice appear in all copies and that both that copyright
|
|
* notice and this permission notice appear in supporting documentation, and
|
|
* that the name of the copyright holders not be used in advertising or
|
|
* publicity pertaining to distribution of the software without specific,
|
|
* written prior permission. The copyright holders make no representations
|
|
* about the suitability of this software for any purpose. It is provided "as
|
|
* is" without express or implied warranty.
|
|
*
|
|
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
|
* OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/kobj.h>
|
|
#include <sys/bus.h>
|
|
#include <dev/iicbus/iic.h>
|
|
#include "iicbus_if.h"
|
|
#include <dev/iicbus/iiconf.h>
|
|
#include <dev/drm2/drmP.h>
|
|
#include <dev/drm2/drm_dp_helper.h>
|
|
|
|
static int
|
|
iic_dp_aux_transaction(device_t idev, int mode, uint8_t write_byte,
|
|
uint8_t *read_byte)
|
|
{
|
|
struct iic_dp_aux_data *aux_data;
|
|
int ret;
|
|
|
|
aux_data = device_get_softc(idev);
|
|
ret = (*aux_data->aux_ch)(idev, mode, write_byte, read_byte);
|
|
if (ret < 0)
|
|
return (ret);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* I2C over AUX CH
|
|
*/
|
|
|
|
/*
|
|
* Send the address. If the I2C link is running, this 'restarts'
|
|
* the connection with the new address, this is used for doing
|
|
* a write followed by a read (as needed for DDC)
|
|
*/
|
|
static int
|
|
iic_dp_aux_address(device_t idev, u16 address, bool reading)
|
|
{
|
|
struct iic_dp_aux_data *aux_data;
|
|
int mode, ret;
|
|
|
|
aux_data = device_get_softc(idev);
|
|
mode = MODE_I2C_START;
|
|
if (reading)
|
|
mode |= MODE_I2C_READ;
|
|
else
|
|
mode |= MODE_I2C_WRITE;
|
|
aux_data->address = address;
|
|
aux_data->running = true;
|
|
ret = iic_dp_aux_transaction(idev, mode, 0, NULL);
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* Stop the I2C transaction. This closes out the link, sending
|
|
* a bare address packet with the MOT bit turned off
|
|
*/
|
|
static void
|
|
iic_dp_aux_stop(device_t idev, bool reading)
|
|
{
|
|
struct iic_dp_aux_data *aux_data;
|
|
int mode;
|
|
|
|
aux_data = device_get_softc(idev);
|
|
mode = MODE_I2C_STOP;
|
|
if (reading)
|
|
mode |= MODE_I2C_READ;
|
|
else
|
|
mode |= MODE_I2C_WRITE;
|
|
if (aux_data->running) {
|
|
(void)iic_dp_aux_transaction(idev, mode, 0, NULL);
|
|
aux_data->running = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Write a single byte to the current I2C address, the
|
|
* the I2C link must be running or this returns -EIO
|
|
*/
|
|
static int
|
|
iic_dp_aux_put_byte(device_t idev, u8 byte)
|
|
{
|
|
struct iic_dp_aux_data *aux_data;
|
|
int ret;
|
|
|
|
aux_data = device_get_softc(idev);
|
|
|
|
if (!aux_data->running)
|
|
return (-EIO);
|
|
|
|
ret = iic_dp_aux_transaction(idev, MODE_I2C_WRITE, byte, NULL);
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* Read a single byte from the current I2C address, the
|
|
* I2C link must be running or this returns -EIO
|
|
*/
|
|
static int
|
|
iic_dp_aux_get_byte(device_t idev, u8 *byte_ret)
|
|
{
|
|
struct iic_dp_aux_data *aux_data;
|
|
int ret;
|
|
|
|
aux_data = device_get_softc(idev);
|
|
|
|
if (!aux_data->running)
|
|
return (-EIO);
|
|
|
|
ret = iic_dp_aux_transaction(idev, MODE_I2C_READ, 0, byte_ret);
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
iic_dp_aux_xfer(device_t idev, struct iic_msg *msgs, uint32_t num)
|
|
{
|
|
u8 *buf;
|
|
int b, m, ret;
|
|
u16 len;
|
|
bool reading;
|
|
|
|
ret = 0;
|
|
reading = false;
|
|
|
|
for (m = 0; m < num; m++) {
|
|
len = msgs[m].len;
|
|
buf = msgs[m].buf;
|
|
reading = (msgs[m].flags & IIC_M_RD) != 0;
|
|
ret = iic_dp_aux_address(idev, msgs[m].slave >> 1, reading);
|
|
if (ret < 0)
|
|
break;
|
|
if (reading) {
|
|
for (b = 0; b < len; b++) {
|
|
ret = iic_dp_aux_get_byte(idev, &buf[b]);
|
|
if (ret != 0)
|
|
break;
|
|
}
|
|
} else {
|
|
for (b = 0; b < len; b++) {
|
|
ret = iic_dp_aux_put_byte(idev, buf[b]);
|
|
if (ret < 0)
|
|
break;
|
|
}
|
|
}
|
|
if (ret != 0)
|
|
break;
|
|
}
|
|
iic_dp_aux_stop(idev, reading);
|
|
DRM_DEBUG_KMS("dp_aux_xfer return %d\n", ret);
|
|
return (-ret);
|
|
}
|
|
|
|
static void
|
|
iic_dp_aux_reset_bus(device_t idev)
|
|
{
|
|
|
|
(void)iic_dp_aux_address(idev, 0, false);
|
|
(void)iic_dp_aux_stop(idev, false);
|
|
}
|
|
|
|
static int
|
|
iic_dp_aux_reset(device_t idev, u_char speed, u_char addr, u_char *oldaddr)
|
|
{
|
|
|
|
iic_dp_aux_reset_bus(idev);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
iic_dp_aux_prepare_bus(device_t idev)
|
|
{
|
|
|
|
/* adapter->retries = 3; */
|
|
iic_dp_aux_reset_bus(idev);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
iic_dp_aux_probe(device_t idev)
|
|
{
|
|
|
|
return (BUS_PROBE_DEFAULT);
|
|
}
|
|
|
|
static int
|
|
iic_dp_aux_attach(device_t idev)
|
|
{
|
|
struct iic_dp_aux_data *aux_data;
|
|
|
|
aux_data = device_get_softc(idev);
|
|
aux_data->port = device_add_child(idev, "iicbus", -1);
|
|
if (aux_data->port == NULL)
|
|
return (ENXIO);
|
|
device_quiet(aux_data->port);
|
|
bus_generic_attach(idev);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
iic_dp_aux_add_bus(device_t dev, const char *name,
|
|
int (*ch)(device_t idev, int mode, uint8_t write_byte, uint8_t *read_byte),
|
|
void *priv, device_t *bus, device_t *adapter)
|
|
{
|
|
device_t ibus;
|
|
struct iic_dp_aux_data *data;
|
|
int idx, error;
|
|
static int dp_bus_counter;
|
|
|
|
mtx_lock(&Giant);
|
|
|
|
idx = atomic_fetchadd_int(&dp_bus_counter, 1);
|
|
ibus = device_add_child(dev, "drm_iic_dp_aux", idx);
|
|
if (ibus == NULL) {
|
|
mtx_unlock(&Giant);
|
|
DRM_ERROR("drm_iic_dp_aux bus %d creation error\n", idx);
|
|
return (-ENXIO);
|
|
}
|
|
device_quiet(ibus);
|
|
error = device_probe_and_attach(ibus);
|
|
if (error != 0) {
|
|
device_delete_child(dev, ibus);
|
|
mtx_unlock(&Giant);
|
|
DRM_ERROR("drm_iic_dp_aux bus %d attach failed, %d\n",
|
|
idx, error);
|
|
return (-error);
|
|
}
|
|
data = device_get_softc(ibus);
|
|
data->running = false;
|
|
data->address = 0;
|
|
data->aux_ch = ch;
|
|
data->priv = priv;
|
|
error = iic_dp_aux_prepare_bus(ibus);
|
|
if (error == 0) {
|
|
*bus = ibus;
|
|
*adapter = data->port;
|
|
}
|
|
mtx_unlock(&Giant);
|
|
return (-error);
|
|
}
|
|
|
|
static device_method_t drm_iic_dp_aux_methods[] = {
|
|
DEVMETHOD(device_probe, iic_dp_aux_probe),
|
|
DEVMETHOD(device_attach, iic_dp_aux_attach),
|
|
DEVMETHOD(device_detach, bus_generic_detach),
|
|
DEVMETHOD(iicbus_reset, iic_dp_aux_reset),
|
|
DEVMETHOD(iicbus_transfer, iic_dp_aux_xfer),
|
|
DEVMETHOD_END
|
|
};
|
|
static driver_t drm_iic_dp_aux_driver = {
|
|
"drm_iic_dp_aux",
|
|
drm_iic_dp_aux_methods,
|
|
sizeof(struct iic_dp_aux_data)
|
|
};
|
|
static devclass_t drm_iic_dp_aux_devclass;
|
|
DRIVER_MODULE_ORDERED(drm_iic_dp_aux, drmn, drm_iic_dp_aux_driver,
|
|
drm_iic_dp_aux_devclass, 0, 0, SI_ORDER_SECOND);
|