/*++ Copyright (c) 2015 Minoca Corp. All Rights Reserved Module Name: tps65217.c Abstract: This module implements support for the TPS65217 Power Management IC. Author: Evan Green 8-Sep-2015 Environment: Kernel --*/ // // ------------------------------------------------------------------- Includes // #include #include #include #include "tps65217.h" // // ---------------------------------------------------------------- Definitions // #define TPS65217_ALLOCATION_TAG 0x35367054 #define TPS65217_MAX_PACKET_SIZE 2 #define TPS65217_DCDC_SETTINGS 64 // // ------------------------------------------------------ Data Type Definitions // typedef enum _TPS65217_PASSWORD_LEVEL { Tps65217PasswordNone, Tps65217PasswordLevel1, Tps65217PasswordLevel2 } TPS65217_PASSWORD_LEVEL, *PTPS65217_PASSWORD_LEVEL; /*++ Structure Description: This structure defines the context for a TPS65217 PMIC. Members: OsDevice - Stores a pointer to the OS device object. InterruptLine - Stores the interrupt line that this controller's interrupt comes in on. InterruptVector - Stores the interrupt vector that this controller's interrupt comes in on. InterruptResourcesFound - Stores a boolean indicating whether or not the interrupt line and interrupt vector fields are valid. InterruptHandle - Stores a pointer to the handle received when the interrupt was connected. Lock - Stores a pointer to a lock serializing access to the controller. SpbResource - Stores a pointer to the Simple Peripheral Bus resource allocation used to connect to the controller. SpbSignedUp - Stores a boolean indicating whether or not interface notifications have been signed up for yet or not. SpbInterface - Stores a pointer to the Simple Peripheral Bus interface used to communicate with the device. SpbHandle - Stores the open handle to the Simple Peripheral Bus for this device. RequestBuffer - Stores a pointer to a buffer of size TPS65217_MAX_PACKET_SIZE used for request data. RequestIoBuffer - Stores a pointer to the I/O buffer around the request buffer. Interface - Stores the interface definition. --*/ typedef struct _TPS65217_CONTROLLER { PDEVICE OsDevice; ULONGLONG InterruptLine; ULONGLONG InterruptVector; BOOL InterruptResourcesFound; HANDLE InterruptHandle; PQUEUED_LOCK Lock; PRESOURCE_ALLOCATION SpbResource; BOOL SpbSignedUp; PSPB_INTERFACE SpbInterface; SPB_HANDLE SpbHandle; PVOID RequestBuffer; PIO_BUFFER RequestIoBuffer; INTERFACE_TPS65217 Interface; } TPS65217_CONTROLLER, *PTPS65217_CONTROLLER; // // ----------------------------------------------- Internal Function Prototypes // KSTATUS Tps65217AddDevice ( PVOID Driver, PSTR DeviceId, PSTR ClassId, PSTR CompatibleIds, PVOID DeviceToken ); VOID Tps65217DispatchStateChange ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); VOID Tps65217DispatchOpen ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); VOID Tps65217DispatchClose ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); VOID Tps65217DispatchIo ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); VOID Tps65217DispatchSystemControl ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); INTERRUPT_STATUS Tps65217InterruptServiceWorker ( PVOID Context ); KSTATUS Tps65217ProcessResourceRequirements ( PIRP Irp ); KSTATUS Tps65217StartDevice ( PIRP Irp, PTPS65217_CONTROLLER Device ); VOID Tps65217SpbInterfaceNotificationCallback ( PVOID Context, PDEVICE Device, PVOID InterfaceBuffer, ULONG InterfaceBufferSize, BOOL Arrival ); KSTATUS Tps65217Initialize ( PTPS65217_CONTROLLER Controller ); VOID Tps65217InterruptThread ( PVOID Parameter ); KSTATUS Tps65217InterfaceSetDcDcRegulator ( PINTERFACE_TPS65217 Interface, TPS65217_DCDC_REGULATOR Regulator, ULONG Millivolts ); KSTATUS Tps65217SetDcDcRegulator ( PTPS65217_CONTROLLER Controller, TPS65217_REGISTER Register, ULONG Millivolts ); KSTATUS Tps65217Write ( PTPS65217_CONTROLLER Controller, TPS65217_REGISTER Register, UCHAR Data ); KSTATUS Tps65217Read ( PTPS65217_CONTROLLER Controller, TPS65217_REGISTER Register, PUCHAR Data ); KSTATUS Tps65217AccessRegister ( PTPS65217_CONTROLLER Controller, TPS65217_REGISTER Register, PUCHAR Data, BOOL Write ); // // -------------------------------------------------------------------- Globals // // // Define the password levels for each register. // const TPS65217_PASSWORD_LEVEL Tps65217PasswordLevel[Tps65217RegisterCount] = { Tps65217PasswordNone, Tps65217PasswordNone, Tps65217PasswordNone, Tps65217PasswordNone, Tps65217PasswordNone, Tps65217PasswordNone, Tps65217PasswordNone, Tps65217PasswordNone, Tps65217PasswordNone, Tps65217PasswordNone, Tps65217PasswordNone, Tps65217PasswordNone, Tps65217PasswordNone, Tps65217PasswordLevel1, Tps65217PasswordLevel2, Tps65217PasswordLevel2, Tps65217PasswordLevel2, Tps65217PasswordLevel2, Tps65217PasswordLevel2, Tps65217PasswordLevel2, Tps65217PasswordLevel2, Tps65217PasswordLevel2, Tps65217PasswordLevel1, Tps65217PasswordNone, Tps65217PasswordLevel1, Tps65217PasswordLevel1, Tps65217PasswordLevel1, Tps65217PasswordLevel1, Tps65217PasswordLevel1, Tps65217PasswordLevel1, Tps65217PasswordLevel1, }; // // Store the conversion from DCDC regulator values to millivolts. // const USHORT Tps65217DcDcMillivolts[TPS65217_DCDC_SETTINGS] = { 900, 925, 950, 975, 1000, 1025, 1050, 1075, 1100, 1125, 1150, 1175, 1200, 1225, 1250, 1275, 1300, 1325, 1350, 1375, 1400, 1425, 1450, 1475, 1500, 1550, 1600, 1650, 1700, 1750, 1800, 1850, 1900, 1950, 2000, 2050, 2100, 2150, 2200, 2250, 2300, 2350, 2400, 2450, 2500, 2550, 2600, 2650, 2700, 2750, 2800, 2850, 2900, 3000, 3100, 3200, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300 }; PDRIVER Tps65217Driver; UUID Tps65217SpbInterfaceUuid = UUID_SPB_INTERFACE; UUID Tps65217InterfaceUuid = UUID_TPS65217_INTERFACE; INTERFACE_TPS65217 Tps65217InterfaceTemplate = { NULL, Tps65217InterfaceSetDcDcRegulator }; // // ------------------------------------------------------------------ Functions // KSTATUS DriverEntry ( PDRIVER Driver ) /*++ Routine Description: This routine is the entry point for the TPS65217. It registers its other dispatch functions, and performs driver-wide initialization. Arguments: Driver - Supplies a pointer to the driver object. Return Value: STATUS_SUCCESS on success. Failure code on error. --*/ { DRIVER_FUNCTION_TABLE FunctionTable; KSTATUS Status; Tps65217Driver = Driver; RtlZeroMemory(&FunctionTable, sizeof(DRIVER_FUNCTION_TABLE)); FunctionTable.Version = DRIVER_FUNCTION_TABLE_VERSION; FunctionTable.AddDevice = Tps65217AddDevice; FunctionTable.DispatchStateChange = Tps65217DispatchStateChange; FunctionTable.DispatchOpen = Tps65217DispatchOpen; FunctionTable.DispatchClose = Tps65217DispatchClose; FunctionTable.DispatchIo = Tps65217DispatchIo; FunctionTable.DispatchSystemControl = Tps65217DispatchSystemControl; Status = IoRegisterDriverFunctions(Driver, &FunctionTable); return Status; } KSTATUS Tps65217AddDevice ( PVOID Driver, PSTR DeviceId, PSTR ClassId, PSTR CompatibleIds, PVOID DeviceToken ) /*++ Routine Description: This routine is called when a device is detected for which this driver acts as the function driver. The driver will attach itself to the stack. Arguments: Driver - Supplies a pointer to the driver being called. DeviceId - Supplies a pointer to a string with the device ID. ClassId - Supplies a pointer to a string containing the device's class ID. CompatibleIds - Supplies a pointer to a string containing device IDs that would be compatible with this device. DeviceToken - Supplies an opaque token that the driver can use to identify the device in the system. This token should be used when attaching to the stack. Return Value: STATUS_SUCCESS on success. Failure code if the driver was unsuccessful in attaching itself. --*/ { UINTN AllocationSize; PTPS65217_CONTROLLER Controller; KSTATUS Status; AllocationSize = sizeof(TPS65217_CONTROLLER) + TPS65217_MAX_PACKET_SIZE; Controller = MmAllocatePagedPool(AllocationSize, TPS65217_ALLOCATION_TAG); if (Controller == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(Controller, AllocationSize); Controller->OsDevice = DeviceToken; Controller->InterruptHandle = INVALID_HANDLE; Controller->RequestBuffer = (PVOID)(Controller + 1); RtlCopyMemory(&(Controller->Interface), &Tps65217InterfaceTemplate, sizeof(INTERFACE_TPS65217)); Status = MmCreateIoBuffer(Controller->RequestBuffer, TPS65217_MAX_PACKET_SIZE, IO_BUFFER_FLAG_KERNEL_MODE_DATA, &(Controller->RequestIoBuffer)); if (!KSUCCESS(Status)) { Status = STATUS_INSUFFICIENT_RESOURCES; goto AddDeviceEnd; } Controller->Lock = KeCreateQueuedLock(); if (Controller->Lock == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto AddDeviceEnd; } Status = IoAttachDriverToDevice(Driver, DeviceToken, Controller); AddDeviceEnd: if (!KSUCCESS(Status)) { if (Controller != NULL) { if (Controller->RequestIoBuffer != NULL) { MmFreeIoBuffer(Controller->RequestIoBuffer); } if (Controller->Lock != NULL) { KeDestroyQueuedLock(Controller->Lock); } MmFreePagedPool(Controller); } } return Status; } VOID Tps65217DispatchStateChange ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles State Change IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { KSTATUS Status; ASSERT(Irp->MajorCode == IrpMajorStateChange); if (Irp->Direction == IrpUp) { switch (Irp->MinorCode) { case IrpMinorQueryResources: Status = Tps65217ProcessResourceRequirements(Irp); if (!KSUCCESS(Status)) { IoCompleteIrp(Tps65217Driver, Irp, Status); } break; case IrpMinorStartDevice: Status = Tps65217StartDevice(Irp, DeviceContext); if (!KSUCCESS(Status)) { IoCompleteIrp(Tps65217Driver, Irp, Status); } break; default: break; } } return; } VOID Tps65217DispatchOpen ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles Open IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { return; } VOID Tps65217DispatchClose ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles Close IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { return; } VOID Tps65217DispatchIo ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles I/O IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { return; } VOID Tps65217DispatchSystemControl ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles System Control IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { ASSERT(Irp->MajorCode == IrpMajorSystemControl); // // Do no processing on any IRPs. Let them flow. // return; } INTERRUPT_STATUS Tps65217InterruptServiceWorker ( PVOID Context ) /*++ Routine Description: This routine represents the low level interrupt service routine for the TPS65217. Arguments: Context - Supplies the context supplied when this interrupt was initially connected. Return Value: Returns an interrupt status indicating if this ISR is claiming the interrupt, not claiming the interrupt, or needs the interrupt to be masked temporarily. --*/ { KSTATUS Status; THREAD_CREATION_PARAMETERS ThreadParameters; RtlZeroMemory(&ThreadParameters, sizeof(THREAD_CREATION_PARAMETERS)); ThreadParameters.ThreadRoutine = Tps65217InterruptThread; ThreadParameters.Parameter = Context; Status = PsCreateThread(&ThreadParameters); if (!KSUCCESS(Status)) { return InterruptStatusNotClaimed; } return InterruptStatusDefer; } // // --------------------------------------------------------- Internal Functions // KSTATUS Tps65217ProcessResourceRequirements ( PIRP Irp ) /*++ Routine Description: This routine filters through the resource requirements presented by the bus for a TPS65217. It adds an interrupt vector requirement for any interrupt line requested. Arguments: Irp - Supplies a pointer to the I/O request packet. Return Value: Status code. --*/ { PRESOURCE_CONFIGURATION_LIST Requirements; KSTATUS Status; RESOURCE_REQUIREMENT VectorRequirement; ASSERT((Irp->MajorCode == IrpMajorStateChange) && (Irp->MinorCode == IrpMinorQueryResources)); // // Initialize a nice interrupt vector requirement in preparation. // RtlZeroMemory(&VectorRequirement, sizeof(RESOURCE_REQUIREMENT)); VectorRequirement.Type = ResourceTypeInterruptVector; VectorRequirement.Minimum = 0; VectorRequirement.Maximum = -1; VectorRequirement.Length = 1; // // Loop through all configuration lists, creating a vector for each line. // Requirements = Irp->U.QueryResources.ResourceRequirements; Status = IoCreateAndAddInterruptVectorsForLines(Requirements, &VectorRequirement); if (!KSUCCESS(Status)) { goto ProcessResourceRequirementsEnd; } ProcessResourceRequirementsEnd: return Status; } KSTATUS Tps65217StartDevice ( PIRP Irp, PTPS65217_CONTROLLER Device ) /*++ Routine Description: This routine starts the TPS65217 PMIC device. Arguments: Irp - Supplies a pointer to the start IRP. Device - Supplies a pointer to the device information. Return Value: Status code. --*/ { PRESOURCE_ALLOCATION Allocation; PRESOURCE_ALLOCATION_LIST AllocationList; IO_CONNECT_INTERRUPT_PARAMETERS Connect; PRESOURCE_ALLOCATION LineAllocation; KSTATUS Status; // // Loop through the allocated resources to get the controller base and the // interrupt. // ASSERT(Device->InterruptHandle == INVALID_HANDLE); Device->InterruptResourcesFound = FALSE; AllocationList = Irp->U.StartDevice.ProcessorLocalResources; Allocation = IoGetNextResourceAllocation(AllocationList, NULL); while (Allocation != NULL) { // // If the resource is an interrupt vector, then it should have an // owning interrupt line allocation. // if (Allocation->Type == ResourceTypeInterruptVector) { LineAllocation = Allocation->OwningAllocation; if (Device->InterruptResourcesFound == FALSE) { ASSERT(Allocation->OwningAllocation != NULL); // // Save the line and vector number. // Device->InterruptLine = LineAllocation->Allocation; Device->InterruptVector = Allocation->Allocation; Device->InterruptResourcesFound = TRUE; } else { ASSERT((Device->InterruptLine == LineAllocation->Allocation) && (Device->InterruptVector == Allocation->Allocation)); } } else if (Allocation->Type == ResourceTypeSimpleBus) { if (Device->SpbResource == NULL) { Device->SpbResource = Allocation; } } // // Get the next allocation in the list. // Allocation = IoGetNextResourceAllocation(AllocationList, Allocation); } if (Device->SpbResource == NULL) { Status = STATUS_NOT_READY; goto StartDeviceEnd; } // // Sign up for interface notifications on the Simple Bus device to get // access to the simple bus interface. This should call back immediately. // if (Device->SpbSignedUp == FALSE) { Status = IoRegisterForInterfaceNotifications( &Tps65217SpbInterfaceUuid, Tps65217SpbInterfaceNotificationCallback, Device->SpbResource->Provider, Device, TRUE); if (!KSUCCESS(Status)) { goto StartDeviceEnd; } Device->SpbSignedUp = TRUE; } // // The device cannot start up if there is no bus interface to talk over. // if (Device->SpbInterface == NULL) { Status = STATUS_NO_INTERFACE; goto StartDeviceEnd; } // // Try to open up communications over the simple bus. // if (Device->SpbHandle == NULL) { ASSERT(Device->SpbResource->DataSize >= sizeof(RESOURCE_SPB_DATA)); KeAcquireQueuedLock(Device->Lock); Status = Device->SpbInterface->Open(Device->SpbInterface, Device->SpbResource->Data, &(Device->SpbHandle)); KeReleaseQueuedLock(Device->Lock); if (!KSUCCESS(Status)) { RtlDebugPrint("TPS65217: Open SPB Failed: %x\n", Status); goto StartDeviceEnd; } } Status = Tps65217Initialize(Device); if (!KSUCCESS(Status)) { goto StartDeviceEnd; } // // Connect the interrupt. // if ((Device->InterruptHandle == INVALID_HANDLE) && (Device->InterruptResourcesFound != FALSE)) { RtlZeroMemory(&Connect, sizeof(IO_CONNECT_INTERRUPT_PARAMETERS)); Connect.Version = IO_CONNECT_INTERRUPT_PARAMETERS_VERSION; Connect.Device = Irp->Device; Connect.LineNumber = Device->InterruptLine; Connect.Vector = Device->InterruptVector; Connect.LowLevelServiceRoutine = Tps65217InterruptServiceWorker; Connect.Context = Device; Connect.Interrupt = &(Device->InterruptHandle); Status = IoConnectInterrupt(&Connect); if (!KSUCCESS(Status)) { return Status; } } // // Publish the interface. // if (Device->Interface.Context == NULL) { Device->Interface.Context = Device; Status = IoCreateInterface(&Tps65217InterfaceUuid, Device->OsDevice, &(Device->Interface), sizeof(Device->Interface)); if (!KSUCCESS(Status)) { Device->Interface.Context = NULL; goto StartDeviceEnd; } } StartDeviceEnd: return Status; } VOID Tps65217SpbInterfaceNotificationCallback ( PVOID Context, PDEVICE Device, PVOID InterfaceBuffer, ULONG InterfaceBufferSize, BOOL Arrival ) /*++ Routine Description: This routine is called to notify listeners that an interface has arrived or departed. Arguments: Context - Supplies the caller's context pointer, supplied when the caller requested interface notifications. Device - Supplies a pointer to the device exposing or deleting the interface. InterfaceBuffer - Supplies a pointer to the interface buffer of the interface. InterfaceBufferSize - Supplies the buffer size. Arrival - Supplies TRUE if a new interface is arriving, or FALSE if an interface is departing. Return Value: None. --*/ { PTPS65217_CONTROLLER Controller; PSPB_INTERFACE Interface; KSTATUS Status; Controller = Context; KeAcquireQueuedLock(Controller->Lock); // // If the interface is arriving, store a pointer to it. // if (Arrival != FALSE) { Interface = InterfaceBuffer; if (InterfaceBufferSize < sizeof(SPB_INTERFACE)) { ASSERT(FALSE); return; } ASSERT(Controller->SpbInterface == NULL); Controller->SpbInterface = Interface; ASSERT(Controller->SpbHandle == NULL); // // If the interface is disappearing, close the handle. // } else { // // First close the published interface. // if (Controller->Interface.Context != NULL) { ASSERT(Controller->Interface.Context == Controller); Status = IoDestroyInterface(&Tps65217InterfaceUuid, Controller->OsDevice, &(Controller->Interface)); ASSERT(KSUCCESS(Status)); Controller->Interface.Context = NULL; } Interface = Controller->SpbInterface; if (Controller->SpbHandle != NULL) { Interface->Close(Interface, Controller->SpbHandle); Controller->SpbHandle = NULL; } Controller->SpbInterface = NULL; } KeReleaseQueuedLock(Controller->Lock); return; } KSTATUS Tps65217Initialize ( PTPS65217_CONTROLLER Controller ) /*++ Routine Description: This routine initializes the TPS65217 PMIC. It reads the chip revision. Arguments: Controller - Supplies a pointer to the controller. Return Value: Status code. --*/ { UCHAR ChipId; UCHAR ChipId2; ULONG LoopIndex; KSTATUS Status; KeAcquireQueuedLock(Controller->Lock); Status = Tps65217Read(Controller, Tps65217ChipId, &ChipId); if (!KSUCCESS(Status)) { goto InitializeEnd; } if ((ChipId == 0) || (ChipId == 0xFF)) { Status = STATUS_DEVICE_IO_ERROR; goto InitializeEnd; } for (LoopIndex = 0; LoopIndex < 20; LoopIndex += 1) { Status = Tps65217Read(Controller, Tps65217ChipId, &ChipId2); if (!KSUCCESS(Status)) { RtlDebugPrint("Failed on loop %d\n", LoopIndex); ASSERT(FALSE); goto InitializeEnd; } if (ChipId2 != ChipId) { RtlDebugPrint("Mismatch (%d) %x %x\n", LoopIndex, ChipId, ChipId2); } } InitializeEnd: KeReleaseQueuedLock(Controller->Lock); return Status; } VOID Tps65217InterruptThread ( PVOID Parameter ) /*++ Routine Description: This routine is the entry point for a new thread that is spawned each time the TPS65217 interrupt fires. Arguments: Parameter - Supplies a context pointer, which in this case points to the controller. Return Value: None. --*/ { PTPS65217_CONTROLLER Controller; UCHAR InterruptRegister; INTERRUPT_STATUS InterruptStatus; KSTATUS Status; InterruptStatus = InterruptStatusNotClaimed; Controller = Parameter; Status = Tps65217Read(Controller, Tps65217Interrupt, &InterruptRegister); if (!KSUCCESS(Status)) { goto InterruptThreadEnd; } RtlDebugPrint("TPS65217 Interrupt %x\n", InterruptRegister); if ((InterruptRegister & TPS65217_INTERRUPT_STATUS_MASK) != 0) { InterruptStatus = InterruptStatusClaimed; } InterruptThreadEnd: HlContinueInterrupt(Controller->InterruptHandle, InterruptStatus); return; } KSTATUS Tps65217InterfaceSetDcDcRegulator ( PINTERFACE_TPS65217 Interface, TPS65217_DCDC_REGULATOR Regulator, ULONG Millivolts ) /*++ Routine Description: This routine sets a TPS65217 DC-DC regulator voltage to the given value. Arguments: Interface - Supplies a pointer to the interface instance. Regulator - Supplies the regulator number to change. Millivolts - Supplies the millivolt value to change to. Return Value: Status code. --*/ { PTPS65217_CONTROLLER Controller; TPS65217_REGISTER Register; KSTATUS Status; Controller = Interface->Context; switch (Regulator) { case Tps65217DcDc1: Register = Tps65217DcDc1Voltage; break; case Tps65217DcDc2: Register = Tps65217DcDc2Voltage; break; case Tps65217DcDc3: Register = Tps65217DcDc3Voltage; break; default: return STATUS_INVALID_PARAMETER; } KeAcquireQueuedLock(Controller->Lock); Status = Tps65217SetDcDcRegulator(Controller, Register, Millivolts); KeReleaseQueuedLock(Controller->Lock); return Status; } KSTATUS Tps65217SetDcDcRegulator ( PTPS65217_CONTROLLER Controller, TPS65217_REGISTER Register, ULONG Millivolts ) /*++ Routine Description: This routine sets a TPS65217 DC-DC regulator voltage to the given value. This routine assumes the controller lock is already held. Arguments: Controller - Supplies a pointer to the controller. Register - Supplies the DC-DC regulator register to set. Millivolts - Supplies the millivolt value to change to. Return Value: STATUS_SUCCESS on success. STATUS_NOT_SUPPORTED if the given voltage value cannot be achieved. Other errors on I/O failure. --*/ { UCHAR Control; ULONG Index; KSTATUS Status; UCHAR Value; // // Convert from millivolts to a register value. // Value = 0; for (Index = 0; Index < TPS65217_DCDC_SETTINGS; Index += 1) { if (Tps65217DcDcMillivolts[Index] == Millivolts) { Value = Index; break; } } if (Index == TPS65217_DCDC_SETTINGS) { return STATUS_NOT_SUPPORTED; } Status = Tps65217Write(Controller, Register, Value); if (!KSUCCESS(Status)) { return Status; } // // Set the GO bit to enact the change. // Status = Tps65217Read(Controller, Tps65217SlewControl, &Control); if (!KSUCCESS(Status)) { return Status; } Control |= TPS65217_SLEW_CONTROL_DCDC_GO; Status = Tps65217Write(Controller, Tps65217SlewControl, Control); if (!KSUCCESS(Status)) { return Status; } return Status; } KSTATUS Tps65217Write ( PTPS65217_CONTROLLER Controller, TPS65217_REGISTER Register, UCHAR Data ) /*++ Routine Description: This routine reads a TPS65217 register over I2C. This routine takes care of the password protocol. Arguments: Controller - Supplies a pointer to the controller. Register - Supplies the register to write. Data - Supplies a pointer where the register contents will be returned on success. Return Value: Status code. --*/ { ULONG LoopIndex; ULONG Loops; UCHAR Password; KSTATUS Status; Loops = 0; if (Register > Tps65217RegisterCount) { return STATUS_INVALID_PARAMETER; } Status = STATUS_SUCCESS; switch (Tps65217PasswordLevel[Register]) { case Tps65217PasswordNone: Status = Tps65217AccessRegister(Controller, Register, &Data, TRUE); break; case Tps65217PasswordLevel1: Loops = 1; break; case Tps65217PasswordLevel2: Loops = 2; break; default: ASSERT(FALSE); Status = STATUS_INVALID_PARAMETER; break; } // // Write the password, then the data. For level 1 registers this only needs // to be done once, but for level 2 registers this needs to be done twice. // for (LoopIndex = 0; LoopIndex < Loops; LoopIndex += 1) { Password = TPS65217_PASSWORD_UNLOCK ^ Register; Status = Tps65217AccessRegister(Controller, Tps65217Password, &Password, TRUE); if (!KSUCCESS(Status)) { return Status; } Status = Tps65217AccessRegister(Controller, Register, &Data, TRUE); if (!KSUCCESS(Status)) { return Status; } } return Status; } KSTATUS Tps65217Read ( PTPS65217_CONTROLLER Controller, TPS65217_REGISTER Register, PUCHAR Data ) /*++ Routine Description: This routine reads a TPS65217 register over I2C. Arguments: Controller - Supplies a pointer to the controller. Register - Supplies the register to read. Data - Supplies a pointer where the register contents will be returned on success. Return Value: Status code. --*/ { return Tps65217AccessRegister(Controller, Register, Data, FALSE); } KSTATUS Tps65217AccessRegister ( PTPS65217_CONTROLLER Controller, TPS65217_REGISTER Register, PUCHAR Data, BOOL Write ) /*++ Routine Description: This routine performs an I2C bus access to get or set a single register. Note that this routine alone is not sufficient to write to many TPS registers, due to the password mechanism. Arguments: Controller - Supplies a pointer to the controller. Register - Supplies the register to read or write. Data - Supplies a pointer to the register content (either the value to write or where the read data will be returned). Write - Supplies a boolean indicating whether to read the register (FALSE) or write the register. Return Value: Status code. --*/ { PUCHAR Buffer; PSPB_INTERFACE Interface; KSTATUS Status; SPB_TRANSFER Transfer[2]; SPB_TRANSFER_SET TransferSet; Buffer = Controller->RequestBuffer; Buffer[0] = Register; RtlZeroMemory(Transfer, sizeof(Transfer)); Transfer[0].Direction = SpbTransferDirectionOut; Transfer[0].IoBuffer = Controller->RequestIoBuffer; Transfer[0].Size = 1; Transfer[1].Direction = SpbTransferDirectionIn; if (Write != FALSE) { Transfer[1].Direction = SpbTransferDirectionOut; Buffer[1] = *Data; } Transfer[1].IoBuffer = Controller->RequestIoBuffer; Transfer[1].Offset = 1; Transfer[1].Size = 1; RtlZeroMemory(&TransferSet, sizeof(TransferSet)); INITIALIZE_LIST_HEAD(&(TransferSet.TransferList)); INSERT_BEFORE(&(Transfer[0].ListEntry), &(TransferSet.TransferList)); INSERT_BEFORE(&(Transfer[1].ListEntry), &(TransferSet.TransferList)); Interface = Controller->SpbInterface; Status = Interface->ExecuteTransferSet(Controller->SpbHandle, &TransferSet); if (!KSUCCESS(Status)) { return Status; } if (Write == FALSE) { *Data = Buffer[1]; } return Status; }